Repository: libvips/nip2 Branch: master Commit: 9b93b0b2ea56 Files: 772 Total size: 9.8 MB Directory structure: gitextract_szh7h6y0/ ├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README.md ├── THANKS ├── TODO ├── autogen.sh ├── configure.ac ├── doc/ │ ├── README │ ├── doc-programmer/ │ │ ├── hierarchy.txt │ │ ├── imageview │ │ ├── makeindex.pl │ │ ├── menu.txt │ │ └── regionview │ └── src/ │ ├── Example.def │ ├── html.cfg │ ├── infrared.tex │ ├── intro.tex │ ├── menus.tex │ ├── mydefs.tex │ ├── nipguide.tex │ ├── program.tex │ ├── reference.tex │ └── tutorial.tex ├── man/ │ ├── Makefile.am │ └── man1/ │ ├── Makefile.am │ └── nip2.1 ├── nip2.appdata.xml ├── nip2.desktop.in ├── nip2.spec.in ├── nip2.xml ├── po/ │ ├── ChangeLog │ ├── LINGUAS │ ├── POTFILES.in │ ├── POTFILES.skip │ ├── POTFILES2 │ ├── en_GB.gmo │ ├── en_GB.po │ ├── malkovich.gmo │ ├── malkovich.po │ └── messages ├── proj/ │ ├── README │ └── src/ │ ├── makefile.msc │ └── nip.rc ├── share/ │ ├── Makefile.am │ └── nip2/ │ ├── Makefile.am │ ├── compat/ │ │ ├── 7.10/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Format.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.12/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Format.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.14/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.16/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.24/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.26/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.28/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.38/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.40/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Magick.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _magick.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.8/ │ │ │ ├── Capture.def │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Format.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Morphology.def │ │ │ ├── Mosaic.def │ │ │ ├── New.def │ │ │ ├── Print.def │ │ │ ├── Resize.def │ │ │ ├── Rotate.def │ │ │ ├── Statistics.def │ │ │ ├── X_ray.def │ │ │ ├── _convert.def │ │ │ ├── _errors.def │ │ │ ├── _generate.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 7.9/ │ │ │ ├── Capture.def │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Format.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Morphology.def │ │ │ ├── Mosaic.def │ │ │ ├── New.def │ │ │ ├── Print.def │ │ │ ├── Resize.def │ │ │ ├── Rotate.def │ │ │ ├── Statistics.def │ │ │ ├── X_ray.def │ │ │ ├── _convert.def │ │ │ ├── _errors.def │ │ │ ├── _generate.def │ │ │ ├── _list.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 8.2/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Magick.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _magick.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 8.3/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Magick.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _magick.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 8.4/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Magick.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _magick.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 8.5/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Magick.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _magick.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── 8.6/ │ │ │ ├── Colour.def │ │ │ ├── Filter.def │ │ │ ├── Histogram.def │ │ │ ├── Image.def │ │ │ ├── Magick.def │ │ │ ├── Makefile.am │ │ │ ├── Math.def │ │ │ ├── Matrix.def │ │ │ ├── Object.def │ │ │ ├── Preferences.ws │ │ │ ├── Tasks.def │ │ │ ├── Widgets.def │ │ │ ├── _Object.def │ │ │ ├── _convert.def │ │ │ ├── _generate.def │ │ │ ├── _joe_extra.def │ │ │ ├── _joe_utilities.def │ │ │ ├── _list.def │ │ │ ├── _magick.def │ │ │ ├── _predicate.def │ │ │ ├── _stdenv.def │ │ │ └── _types.def │ │ ├── Makefile.am │ │ └── _compat.def │ ├── data/ │ │ ├── AdobeRGB1998.icc │ │ ├── Makefile.am │ │ ├── cmyk.icm │ │ ├── examples/ │ │ │ ├── 1_point_mosaic/ │ │ │ │ └── 1pt_mosaic.ws │ │ │ ├── 2_point_mosaic/ │ │ │ │ └── 2pts_mosaic.ws │ │ │ ├── businesscard/ │ │ │ │ └── businesscard.ws │ │ │ ├── clone/ │ │ │ │ └── clone.ws │ │ │ ├── framing/ │ │ │ │ └── framing.ws │ │ │ ├── logo/ │ │ │ │ └── logo2.ws │ │ │ ├── manual_balance/ │ │ │ │ └── manual_balance.ws │ │ │ ├── overlays_and_blending/ │ │ │ │ └── overlay_blend.ws │ │ │ ├── print_test_image.v │ │ │ └── registering/ │ │ │ └── registering.ws │ │ ├── macbeth_lab_d50.mat │ │ ├── macbeth_lab_d65.mat │ │ ├── rachel.con │ │ └── sRGB.icm │ ├── rc/ │ │ ├── Makefile.am │ │ └── ipgtkrc │ └── start/ │ ├── Colour.def │ ├── Filter.def │ ├── Histogram.def │ ├── Image.def │ ├── Magick.def │ ├── Makefile.am │ ├── Math.def │ ├── Matrix.def │ ├── Object.def │ ├── Preferences.ws │ ├── Tasks.def │ ├── Widgets.def │ ├── _Object.def │ ├── _convert.def │ ├── _generate.def │ ├── _joe_extra.def │ ├── _joe_utilities.def │ ├── _list.def │ ├── _magick.def │ ├── _predicate.def │ ├── _stdenv.def │ └── _types.def ├── src/ │ ├── BITMAPS/ │ │ ├── Makefile.am │ │ ├── ant.xbm │ │ ├── automatic.xbm │ │ ├── automatic.xpm │ │ ├── automatic1.xbm │ │ ├── automatic1.xpm │ │ ├── automatic2.xbm │ │ ├── automatic2.xpm │ │ ├── automatic3.xbm │ │ ├── automatic3.xpm │ │ ├── book_closed.xpm │ │ ├── book_open.xpm │ │ ├── change.xbm │ │ ├── col.xpm │ │ ├── convol.xbm │ │ ├── dropper.xpm │ │ ├── dropper_msk.xbm │ │ ├── dropper_src.xbm │ │ ├── floppy.xpm │ │ ├── image.xbm │ │ ├── kill.xbm │ │ ├── mag_msk.xbm │ │ ├── magin.xpm │ │ ├── magin_src.xbm │ │ ├── magout.xpm │ │ ├── magout_src.xbm │ │ ├── mini_page.xpm │ │ ├── morph.xbm │ │ ├── paint.xpm │ │ ├── pan.xpm │ │ ├── program.xbm │ │ ├── select.xpm │ │ ├── separator.xpm │ │ ├── slider.xbm │ │ ├── toolbox_closed.xpm │ │ ├── toolbox_open.xpm │ │ ├── tools.xpm │ │ ├── watch_1.xbm │ │ ├── watch_2.xbm │ │ ├── watch_3.xbm │ │ ├── watch_4.xbm │ │ ├── watch_5.xbm │ │ ├── watch_6.xbm │ │ ├── watch_7.xbm │ │ ├── watch_8.xbm │ │ └── watch_msk.xbm │ ├── Makefile.am │ ├── action.c │ ├── action.h │ ├── boxes.c │ ├── boxes.h │ ├── builtin.c │ ├── builtin.h │ ├── cache.c │ ├── cache.h │ ├── call.c │ ├── call.h │ ├── class.c │ ├── class.h │ ├── classmodel.c │ ├── classmodel.h │ ├── clock.c │ ├── clock.h │ ├── colour.c │ ├── colour.h │ ├── colourdisplay.c │ ├── colourdisplay.h │ ├── colourview.c │ ├── colourview.h │ ├── column.c │ ├── column.h │ ├── columnview.c │ ├── columnview.h │ ├── compile.c │ ├── compile.h │ ├── conversion.c │ ├── conversion.h │ ├── conversionview.c │ ├── conversionview.h │ ├── defbrowser.c │ ├── defbrowser.h │ ├── doubleclick.c │ ├── doubleclick.h │ ├── dummy.c │ ├── dump.c │ ├── dump.h │ ├── editview.c │ ├── editview.h │ ├── error.c │ ├── error.h │ ├── expr.c │ ├── expr.h │ ├── expression.c │ ├── expression.h │ ├── expressionview.c │ ├── expressionview.h │ ├── filemodel.c │ ├── filemodel.h │ ├── filesel.c │ ├── filesel.h │ ├── floatwindow.c │ ├── floatwindow.h │ ├── fontname.c │ ├── fontname.h │ ├── fontnameview.c │ ├── fontnameview.h │ ├── formula.c │ ├── formula.h │ ├── graphicview.c │ ├── graphicview.h │ ├── graphwindow.c │ ├── graphwindow.h │ ├── group.c │ ├── group.h │ ├── gtkutil.c │ ├── gtkutil.h │ ├── heap.c │ ├── heap.h │ ├── heapmodel.c │ ├── heapmodel.h │ ├── helpindex.h │ ├── iarrow.c │ ├── iarrow.h │ ├── icontainer.c │ ├── icontainer.h │ ├── idialog.c │ ├── idialog.h │ ├── iimage.c │ ├── iimage.h │ ├── iimageview.c │ ├── iimageview.h │ ├── imagedisplay.c │ ├── imagedisplay.h │ ├── imageheader.c │ ├── imageheader.h │ ├── imageinfo.c │ ├── imageinfo.h │ ├── imagemodel.c │ ├── imagemodel.h │ ├── imagepresent.c │ ├── imagepresent.h │ ├── imageview.c │ ├── imageview.h │ ├── iobject.c │ ├── iobject.h │ ├── ip.h │ ├── iregion.c │ ├── iregion.h │ ├── iregiongroup.c │ ├── iregiongroup.h │ ├── iregiongroupview.c │ ├── iregiongroupview.h │ ├── iregionview.c │ ├── iregionview.h │ ├── istring.h │ ├── itext.c │ ├── itext.h │ ├── itextview.c │ ├── itextview.h │ ├── iwindow.c │ ├── iwindow.h │ ├── lex.l │ ├── link.c │ ├── link.h │ ├── log │ ├── log.c │ ├── log.h │ ├── main.c │ ├── main.h │ ├── mainw.c │ ├── mainw.h │ ├── makehelpindex.pl │ ├── managed.c │ ├── managed.h │ ├── managedfile.c │ ├── managedfile.h │ ├── managedgobject.c │ ├── managedgobject.h │ ├── managedgvalue.c │ ├── managedgvalue.h │ ├── managedstring.c │ ├── managedstring.h │ ├── matrix.c │ ├── matrix.h │ ├── matrixview.c │ ├── matrixview.h │ ├── model.c │ ├── model.h │ ├── nip2-cli.c │ ├── nip2-icon.rc │ ├── nipmarshal.c │ ├── nipmarshal.h │ ├── nipmarshal.list │ ├── number.c │ ├── number.h │ ├── numberview.c │ ├── numberview.h │ ├── option.c │ ├── option.h │ ├── optionview.c │ ├── optionview.h │ ├── paintboxview.c │ ├── paintboxview.h │ ├── pane.c │ ├── pane.h │ ├── panechild.c │ ├── panechild.h │ ├── parse.y │ ├── parser.h │ ├── path.c │ ├── path.h │ ├── pathname.c │ ├── pathname.h │ ├── pathnameview.c │ ├── pathnameview.h │ ├── plot.c │ ├── plot.h │ ├── plotmodel.c │ ├── plotmodel.h │ ├── plotpresent.c │ ├── plotpresent.h │ ├── plotstatus.c │ ├── plotstatus.h │ ├── plotview.c │ ├── plotview.h │ ├── plotwindow.c │ ├── plotwindow.h │ ├── popupbutton.c │ ├── popupbutton.h │ ├── predicate.c │ ├── predicate.h │ ├── prefcolumnview.c │ ├── prefcolumnview.h │ ├── prefs.c │ ├── prefs.h │ ├── prefworkspaceview.c │ ├── prefworkspaceview.h │ ├── preview.c │ ├── preview.h │ ├── program.c │ ├── program.h │ ├── progress.c │ ├── progress.h │ ├── real.c │ ├── real.h │ ├── reduce.c │ ├── reduce.h │ ├── regionview.c │ ├── regionview.h │ ├── rhs.c │ ├── rhs.h │ ├── rhsview.c │ ├── rhsview.h │ ├── row.c │ ├── row.h │ ├── rowview.c │ ├── rowview.h │ ├── secret.c │ ├── secret.h │ ├── slider.c │ ├── slider.h │ ├── sliderview.c │ ├── sliderview.h │ ├── spin.c │ ├── spin.h │ ├── statusview.c │ ├── statusview.h │ ├── string.c │ ├── stringview.c │ ├── stringview.h │ ├── subcolumn.c │ ├── subcolumn.h │ ├── subcolumnview.c │ ├── subcolumnview.h │ ├── symbol.c │ ├── symbol.h │ ├── toggle.c │ ├── toggle.h │ ├── toggleview.c │ ├── toggleview.h │ ├── tool.c │ ├── tool.h │ ├── toolkit.c │ ├── toolkit.h │ ├── toolkitbrowser.c │ ├── toolkitbrowser.h │ ├── toolkitgroup.c │ ├── toolkitgroup.h │ ├── toolkitgroupview.c │ ├── toolkitgroupview.h │ ├── toolkitview.c │ ├── toolkitview.h │ ├── toolview.c │ ├── toolview.h │ ├── trace.c │ ├── trace.h │ ├── tree.c │ ├── tree.h │ ├── tslider.c │ ├── tslider.h │ ├── util.c │ ├── util.h │ ├── value.c │ ├── value.h │ ├── valueview.c │ ├── valueview.h │ ├── vector.c │ ├── vector.h │ ├── view.c │ ├── view.h │ ├── vipsobject.c │ ├── vipsobject.h │ ├── vobject.c │ ├── vobject.h │ ├── watch.c │ ├── watch.h │ ├── workspace.c │ ├── workspace.h │ ├── workspacedefs.c │ ├── workspacedefs.h │ ├── workspacegroup.c │ ├── workspacegroup.h │ ├── workspacegroupview.c │ ├── workspacegroupview.h │ ├── workspaceroot.c │ ├── workspaceroot.h │ ├── workspaceview.c │ └── workspaceview.h └── test/ ├── Makefile.am ├── extras/ │ ├── test-5x5.v │ ├── test_magick2.ws │ ├── test_recomp_order.ws │ └── test_transform.ws ├── test_all.sh.in └── workspaces/ ├── big_and_small_disks.ws ├── gold_sample.v ├── test_colour.ws ├── test_conv.ws ├── test_fail.ws ├── test_filter.ws ├── test_fourier.ws ├── test_histogram.ws ├── test_image.ws ├── test_math.ws ├── test_matrix.ws ├── test_snip.def ├── test_stats.ws ├── test_tasks.ws └── test_widgets.ws ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ INSTALL test-driver test/test-suite.log test/test_all.sh.log test/test_all.sh.trs po/nip2.pot nip2-*.tar.gz *.o .*.swp TAGS tags Makefile scan Makefile.in config.* autom4te.cache aclocal.m4 m4/ compile configure depcomp install-sh libtool ltmain.sh missing mkinstalldirs nip2.desktop nip2.spec po/POTFILES fred po/Makefile.in.in src/.deps/ src/gtksheet-marshal.c src/gtksheettypebuiltins.c src/lex.c src/nip2 src/parse.c src/parse.h stamp-h1 test/test_all.sh ylwrap doc/html/ doc/pdf/ ================================================ FILE: AUTHORS ================================================ Authors of nip2 John Cupitt Joe Padfield Hans Breuer Rich Lott Leo Davidson Plus helpful comments and suggestions from many others. ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: ChangeLog ================================================ started 8.9.2 22/11/23 - fix a lockup with comment characters in REPL [MvGulik] started 8.9.1 15/2/23 - fix build with --std=c99 [Schamschula] started 8.9.0 10/4/20 - add find_trim started 8.7.1 13/11/18 - fix uint status bar pixels >2**31 [Rob Erdmann] - fix crash on redhat [bgilbert] started 8.7.0 22/5/18 - added vips7compat.h include for libvips 8.7 - more output for -V to help debugging CLI mode - revised Text widget - added Canny - Sobel uses the new vips_sobel() operator - add mitchell kernel - add 8.6 compat started 8.6.1 1/5/18 - better enum display in header started 8.6.0 16/8/17 - add scRGB support - improve radiance support - add composite to alpha menu - add Image / Select / Fill - add combine mode to indexed histogram - better compat handling started 8.5.1 22/1/17 - fix a crash bug - make separate Image / Alpha menu, add Add, Extract, Drop started 8.5 24/1/17 - add max_slope to lhist - gaussnoise goes via vips8 now - add snake option to array join [Joe Padfield] - parse_float was broken for numbers starting "0." - add alpha section to Image / Band menu ... Flatten, Premultiply, Unpremultiply, Blend - add Entropy to hist menu started 8.4.1 25/9/16 - simplify nip2-icon.rc build, bgilbert started 8.4 - added Perlin and Worley menu items started 8.3.1 on 19/5/16 - disable debug by default, thanks Benjamin - configure changes to help win64 - improve middle-drag in ws and image view - be more careful about the name of the image file we remove on close - simpler system for positioning new columns - rename boostrap.sh as autogen to help snapcraft started 8.3.0 on 28/3/16 - move path search stuff into _convert from _magick - added autotrace menu item - resize now uses vips_resize() behind the scenes - added Kernel type for picking interpolators started 8.2.1 on 4/12/16 - tiny improvement to idle handling - added R2 to linreg and linregw - fixed vips_call for image array args - changed default unsharp settings to be less brutal and to ban -ve sharpness started 8.2 on 4/11/15 - version bump to match vips - fix icc_import with RGBA images - added mapim and Image / Transform / Map - added Filter / Coordinate Transform ... polar and rect in there started 8.0 on 3/5/15 - version bump for vips-8.0 release - fix a race in Makefile.am, thanks nieder - get rid of run-nip2.sh, mostly useless, thanks nieder started 7.42.1 started 30/12/14 - add fftw3 configure - fix gvc configure started 7.42.0 started 4/11/14 - removed the non-nip2 bits of the test suite, they are in vips now started 7.41.0 8/10/14 - remove greyc stuff started 7.40.5 17/9/14 - improve .desktop file - fix Lch -> Yxy conversion started 7.40.4 19/8/14 - swap the HP printer profile for a freer one - swap lena for a PD sample started 7.40.3 4/7/14 - fix compile with older libvipes, now goes back to at least 7.30 - fix more bash-isms to help freebsd - don't test IM by default, in case it's not installed - get graph display working again with latest libgvc started 7.40.2 30/6/14 - fix quoting in magick commands - auto-fallback to gm if no convert found - use libxml2 pretty-printer started 7.40.1 24/6/14 - update copyright date - larger max size for dialog text - fix popen()pclose() warnings on win started 7.40.0 23/6/14 - version bump started 7.39.0 28/1/14 - add optional libgsf dependency - added export-to-file to plotwindow - added graph_export_image - added .to_image to Plot - added .caption / .xcaption / .ycaption options to Plot - added caption / xcaption / ycaption options to Plot_object - added snibgo's much better ImageMagick menu items - added test_magick.ws to make check - added series_captions option to Plot_object - support imagevec as a vips_call argument - added system2 and system3 - added Magick.version detector - better image cache menu item - added hough_line and hough_circle - removed tear-off menus, gtk+ has deprecated them started 7.38.4 23/6/14 - fix memccpy() in tool.c, thanks khindenburg started 7.38.3 16/5/14 - fix tiny timeout error started 7.38.2 21/1/14 - fix a tiny mem leak started 7.38.1 20/1/14 - fix scRGB display started 7.38.0 18/1/14 - version bump started 7.36.6 9/1/14 - fix some clang warnings - add some brackets to find_colour-calib, seems to help clang builds with optimiser, strangely started 7.36.5 19/12/13 - add "merge into ws" item to rmb tab gutter menu - oops, progress feedback was accidentally disabled - error boxes were accidentally supressed started 7.36.4 18/10/13 - fix bootstrap warnings - use g_mkdir() - better load of workspaces with closed columns started 7.36.2 8/10/13 - add --profile option - fix + button on wsgv started 7.36.1 7/10/13 - better ^Q behaviour, thanks MvGulik started 7.36.0 3/10/13 started 7.35.0 9/8/13 - removed old thing to show API docs (thanks Benjamin) - measure now lets you pick the area to measure, and draws sample patches - new column now goes to right of current column, not alphabetically - detect doubleclick on ws tab label background - tabs can be locked - tabs have error indicators started 7.34.1 28/6/13 - fix build on older gtk, thanks Joe started 7.34.0 7/6/13 - version bump - drag col to far left to insert - fix compat warning text - fix prefs revert to default - reenable scroll-wheel slider change in paintbox and conversionview - insert new columns in alphabetical position started 7.33.0 14/3/13 - add tabs - get_* work on Groups - columns snap to a grid started 7.32.2 12/3/13 - add a test for seq mode started 7.32.1 7/3/13 - remove "fred" from dist - license updates, thanks Benjamin started 7.32.0 22/1/13 - added colour temperature to colour and colour to colour temperature - removed gtksheet, broken on windows, no future on anywhere - added histogram invert - much better Matrix / New items started 7.31 3/9/12 - don't show tooltips for toolkit menu items with submenus (thanks MvGulik) - better definition of foldr1 - better definition of to_group (thanks MvGulik) - better defintion of scan, renamed as scanl - don't clear def browser filter on text buffer ::changed in program window - update program window filter on cursor move started 7.30.2 24/12/12 - small fix for OS X ML started 7.30.1 7/8/12 - update rectangle select (thanks Joe) - group save was broken (thanks John VV) started 7.30.0 20/7/12 - update for new version started 7.29.0 20/6/12 - added skew and kurtosis - added Definition Browser to program window, shows stuff as you program - program window cleanups - Find calib is much faster and handles linear float input better - Find calib optionally leaves brightness untouched - Apply calib handles linear float input better - add a 7.28 compat area started 7.28.5, 8/5/12 - change keybinding for Delete to ctrl+bsp to work around a GTK bug - rewrite filenames on workspace load, file selection and file drag-drop started 7.28.4, 6/5/12 - added bigtiff save option started 7.28.3, 17/4/12 - up max size of user defs, lets you work with larger groups started 7.28.2, 10/4/12 - complex constant divided by real constant was wrong - more self-tests - disable the libvips operation cache, it doesn't know about invalidate and breaks various things started 7.28.1, 12/3/12 - oop, add Array to private Type decoder (thanks MvGulik) - new version of Draw / Scale (thanks Joe) started 7.28.0, 30/1/12 - bump for new stable version - better "make check" - disable asserts and cast checks in production builds - much faster draw_rect - remove background stipple from image display (helps win32) - Draw / Rect lets you adjust line thickness - added Draw / Scale (thanks Joe) - added VipsStats test (thanks Rebecca) started 7.27.0, 23/8/11 - bump for new cycle - add raw load/save test - test fits load/save - better image header display, now on right-click rowview menu - search image header - rmb popup menu on imageview windows - popup menu button widget - added "vips_call" builtin to call any vips8 operation - added Matrix / New / Series - added Matrix / Sort started 7.26.5, 31/12/11 - fix possible security thing in yyerror(), thanks Jay started 7.26.4, 14/9/11 - better error messages for print-main started 7.26.3, 15/8/11 - tidier cancel messages - disable dump.c debug started 7.26.2, 10/8/11 - update threading test for fixed benchmark - fix blocking in progress update (thanks M. v. Gulik) - search returns empty list for file not found rather than throwing an exception - magick_command tries to use $VIPSHOME/bin/convert.exe, if it exists - magick_command tries quotes filenames started 7.26.1, 28/7/11 - much better threading test, based on im_benchmarkn() started 7.26.0, 26/7/11 - version bump started 7.25.0, 7/1/11 - version bump - oop spelling - fix a crash with resizing dirty matrices - minor fix to vips_call error messages - more tests in "make check" - moved vips_cache and vips_call out of the vips_ namespace - added approx. option to blur/sharpen - added Matrix / New Circular / Square / Identity - removed the splash screen, all machines are fast enough now - added EXEEXT env var - better "bad superclass" error - changed order of args for Option_enum - added Option_list - added a simple Magick menu - better compat handling - added a 7.24 compat dir - removed the "already open for read" error on save, too annoying for the small amount of safety it gave you - test pfm load/save - also test cmyk jpeg/tif load/save - allow file modes in filenames, so "nip2 wtc_pyr.tif:2" works - show main window much sooner during workspace load and startup - better progress feedback - added Image / Select / Rectangle - added Image / Draw menu started 7.24.0, 30/11/10 - bump for 7.24 - fix build without graphviz - much faster colour atlas menu item - fix make check, again - fix debug everywhere - fix a va_args problem on Windows started 7.23.0, 2/8/10 - fix a crash in thumbnail preview with large images - doublelick while painting with a rect (eg. text) would crash (thanks M.v.Gulik) - drag multiple workspaces to the mainw could get stuck (thanks M.v.Gulik) - find-again before find would crash (thanks M.v.Gulik) - open multiple ws in file browser would crash - added filemodel_set_window_hint() and filemodel_get_window_hint() to help ^Q display popups on the right window - split vips_call.c to vips_call / vips_cache - added IM_TYPE_RW support to vips_call.c: you can call paintbox operations directly now - gtk_window_present() parents when we show children in iwindow.c - fix a crash with win32 and two PRESS on a window while in rect mode (thanks M.v.Gulik) - fix a crash with duplicate Colour (thanks M.v.Gulik) - fix an occasional crash with ^Q in imageview - added high-quality thumbnail option (thanks Martin) - set lib env var more carefully (thanks Jay) - configure tests for libgvc, the graphviz library - added "Workspace as graph" view option - better "segment" menu item - "value" menu item - rename stuff to avoid name clashes with cfitsio - "make check" runs twice, with and without vector stuff - better infobar behaviour - test_conv.ws tests convolution carefully - nib radius slider replaces the old 1-10 dropdown, nibs above radius 0 are anti-aliased - changes to help rhel5 - oop, could delete vips files accidentally - better file search started 7.22.2, 5/7/10 - show nthreads in space free tooltip - fix win32 button order, again - fix duplicate workspace - added ^Q, for quit nip2, to all windows - rename gtk_entry_*() to gtk_item_entry_*() in gtkitementry.c, thanks Adam started 7.22.1, 13/6/10 - relax tolerances in test_colour.ws, thanks Peter - improve region repaint during drag, thanks Ruven - test relational constants - test load / save in various file formats - test threading system - removed malkovich locale, oops started 7.22.0, 12/5/10 - version bump - gtksheet sizing changes, again - plot window destroy cleanup started 7.21.0, 8/12/09 - 7.16 ws load could fail (thanks Jim) - "make check" tests the example workspaces too - nip2-cli.c improvements (thanks Leo) - leak test improvements - set double-click time from the system - don't copy to file for paintbox, it makes dangling pointers if you use it in complex workspaces - thumbnail updates on paint actions, woo - rect and text tools have a working preview box - safer handling of missing exprs in formula - handle im_invalidate() in paintbox ourselves - much faster and smarter image window repaints, especially with the paintbox active - #CPUs in prefs defaults to zero, meaning autodetect - works without GtkInfoBar - better show/hide behaviour for paned - progress feedback for paintbox open started 7.20.5, 27/11/09 - fixed up GtkInfoBar support - oop, help was rather broken started 7.20.4, 26/11/09 - removed 'browse thumbnails' button from filesel - added 'preview' widget to file open - added some basic GtkInfoBar support started 7.20.3, 25/11/09 - argh, button order error in dialogs on win32 - updated help index - initial window size was too large started 7.20.2, 11/11/09 - make GRegex optional so we can work with older glibs - fix a crash with "-p" and Managedstring started 7.20.1, 11/11/09 - add "convf" operator - default number of CPUs bumped to 4 - plot.c can work with goffice-0.7.15 started 7.20.0, 9/11/09 - version bump - "make dist" fixes started 7.19.0 - remove deprecated use of GtkList in option edit ... needs replacing - dropped in new Joe defs (thanks Joe) - reverse dialog button order on win32 - fix memleak with IMAGEVEC args to VIPS - _check_all etc. no longer chain up, for a slight speed increase - fix crash with "" as LHS for various copy operations, eg. ("" ++ "a") - add test_snip.def to test language features - replace-from-file marks a workspace as modified - Arrow and Mark grab handles improved - "don't attach a profile" option for jpeg save - fixes for gtkdoc merge - set TMPDIR on startup to help im_system() - add RAD as a coding type (thanks Roland) - add Filter / Morphology / Segment menu item - much faster meanze for 8 & 16-bit unsigned images - added a "rotate" option to custom convolution - added Histogram / Find / Indexed - Cache defaults to 128x128 tiles - use libgoffice to display plots - use new gtksheet widget, fall back to treeview if we can't build it - better regexp searching in Program window (now full PCRE) - oop horrible tree_map() bug with uops caused a variety of strangeness - group image save now sets image save options (thanks Joe) - sum and product now work for any object - don't set non-existant properties in vips_object_new - faster constant image maker with im_embed() - added "join image array from list", thanks Joe - phew, label backgrounds are back - added raw import (thanks Jim) started 7.18.0 - bumped version numbers - added 7.16 compat mode - revised manual - added snohalo1 wrapper - dropper did not update inkwell picture - better button colour changes - fix examples started 7.17.2 - added progress.[hc] for a better progress/cancel system (again) - splash screen uses new progress system - added list delete, difference, "--" operator - fixed a bug with startup recomps not happening (it was trying to do them in the background, argh) - buildlut makes Plot, not Image - much faster gaussian mask build for large masks - "Size To" has a "break aspect ratio" option - better error message for "[1, 2] < [3, 4]" - support RAD coding - added Radiance menu started 7.17.0 - merged 7.16 branch back into trunk - bumped version number - manual version number was wrong - removed vips8 link, we've started moving that stuff into vips7 now - patches for ubuntu 8.10 - added yafr interp - rotate etc. now have an interp param - revised "resize" to use new modes - transform menu items have inter options - configure fails if bison is not found - fix the filesel filter after a filename change - nicer message on cancel - new VipsFormat stuff - LEXLIBS->LEXLIB (thanks Adam) - added Managedstring, removed old static string system - caption columns can be null, display "doubleclick to edit .." message if they are - added vipsobject builder from old call8 code - added vips_object_new builtin - moved BufInfo down into vips - added IM_INTERPOLATE to vips_call - added Interpolate class and Interpolate_picker - error window output no longer truncates on symbols with many errors - renamed 'Recover After Crash' as 'Search for Workspace Backups' - better Scale alignment in display - regions and scales default to live dragging - better image display defaults (no ruler, no display bar etc.) - thumbnails are transparent when you drag them - side panes have titlebars and close buttons - block attempts to OK on directories in file dialogs - we have a copyright symbol! nicer 'about' box too - better region label positioning - double images ignored rgb16/grey16 hints - configure dies if flex/lex not found - Managedgobject."property" works - added (dir gtype), (dir gobject) - oops, rank filters were off by one by default started 7.16.3 - fixed cancel system (again) started 7.16.2 - argh, "-o" was broken - oops, some left over code for function overloading in the parser - init builtins earlier, so we can spot accidental redefinition - added a NULL type, Group now uses it to indicate an empty slot - another stab at fixing the order of startup actions started 7.16.1 - better pointer set - fixed a couple of notify snafus - use g_assert() instead of assert() to avoid abort() death branch for 7.16 - bumped version - grey16/rgb16 not always set on colour space conversion - revamped test system - set GValue strings as refstrings - try to transform gvalues we get to strings, if we can - added Joe's shrink within macro - removed the last of the fade stuff for faster repaint - open multiple now makea a group, so we can process more files at once - revamped 'make check', much nicer and more useful - better file type guessing - better progress feedback - revised image write code gives better feedback - better group-save, again - fixed a problem with recalc backtracking started 7.15.0 - fixed segv with making tools for non-toplevels - expand the heap if more than 50% full after a GC (was 70%) - added nip2-cli.c (thanks Leo) - updated README - more HIGgy titlebar text in mainw/program - refactor: IWINDOW_TRUE/_FALSE renamed to _YES/_NO - adjustable panes in image header view - histdif was broken for unsigned image types - fix memleak in compile_lcomp() - better --help text - get rid of intltool - use g_idle_add() instead of GAsyncQueue for render notify - fix a segv in imageview destroy - == did not always find the best method - better time debugging in symbol recalc - added $var for string constants - added s => v syntax - fixed recursive invocation bug in vips_call.c - much better hashing of vips calls - configure shows a summary at the end - syntax change :-( lcomps now use [expr :: generators] to reduce ambiguity the old syntax failed for things like [a || b | a <- [true]; b <- [false]] - added --test, so we can check test_toolkits automatically - added --prefix, so we can run without installing - added "make check" support - got rid of the annoying progress popup, it's back in the status bar now - status bar tells you which sym it is computing - revised busy system does all busy feedback - configure switch to stop update of desktop database (thanks Adam) - vips_call hashing improvements - images are GCd after 60s of inactivity, rather than immediately, giving the call cache a chance to revive them ... speedup in some cases - added a 7.14 compat area - join_lr/_tb args swapped - check_args now does not recurse up a class, instead all _check members have to chain up ... a bit quicker - watch "invalidate" in vips_call.c cache ... so paint actions now decache indirect results as well - added Math / Cluster, though it needs a bit of work - insert now format-alikes - another go at removing refresh flicker ... region dragging flickers a bit instead - added Image / Header / Get / Custom - recurse for save groups of groups - added Image / Cache menu item - merged loadable-formats branch started 7.14.0 - updated docs - added 7.12 compat - check for update-mime-database and friends (thanks Tom) - more leak fixes - updated examples and prefs workspaces - break _Object.def out of _types.def - better "if image then constant else constant" behaviour - fixed segvs with IMAGE lifetime and progress dialogs - use xdg-open to show help pages, if available - fixed segvs with IMAGE lifetime and progress dialogs - fixed segvs with outdated iimage change callbacks - some tweaking of the toolkits menu - fixed another lcomp bug - intercept from greyscale option in find_calib - removed unnecessaary assert() from parser - recomp all on startup even in batch mode fixes some strange bugs - apply calib works for groups of images - renamed Error as iError to help windows - more small windows fixes - more small os x fixes started 7.13.3 - save image was broken, weakrefs were not being updated - wrongly setting vips-7.8 region compat mode on all old WS load - "Close" in ws defs pane menu was not working - removed image window / plot window transient-for behaviour, we lost maximise buttons :( - block ungroup of things larger than 100 elements - allow +/- for zoom in and out shortcuts - mainw rmb menu has open/merge items - merge ws doesn't add extra space - added LHS patterns, eg. "[a, b] = fred 12;" - better spacing in merge ws / load ws - added is_list_len and friends ... faster then len for long lists - compile on demand, saves 25% of startup time - now bison only, we won't work with yacc (will package deps need updating?) - added lcomp patterns, eg. "[x*y|[x,y]<-zip2[1..10][11..20]]" - use LHS patterns in defs - oop, dist typechecking could segv - split trace.c to make log.c, base class for logging windows - added error ... error logging window - added destory_if_destroyed() and done some cleanups - oop, im_and_image etc. refs remaining in compat - disallow const-only LHS patterns, eg. "12 = fred;" started 7.13.2 - remove Application from nip2.desktop.in - revised progress system ... works for "max" now! - fix reporting of parse errors in inner scopes - lcomps now nest correctly ... try "Matrix [[x*y|x<-[1..10]]|y<-[1..10]]" - workspaces loaded from stdin with -w save more sensibly started 7.13.1 - you can type "fred = 12" into a columnview, woo - gah, lcomps had "undefined" set on various members because of trimming off parser temps - more visible arrow dashes - added pane.[hc] - added a left pane to mainw to hold ws-local defs - ws-local defs sort-of work - added workspacedefs.[hc] - print all workspace mains on exit too - renamed lor/land as any/all, in line with Haskell - added INTVEC and DOUBLEVEC output - added greyc filter - added "--set" command-line option - better left/right pane widget - resize tk browser search box with pane - nicer widget colour change ... use "*xx*" in style file rather than setting names and contained names started 7.13.0 - woo, fork for new development version - started cleaning up parse.y - simpler DOT syntax ... A1."poop" works now - added lambdas ... \x x + 1 - added listcomp syntax ... [x | x <- [1..]; x > 12] - recomb was broken for >3 band images (thanks km) - lambdas were not being marked as locals correctly, oops - added listcomp code generator started 7.12.5 - tiny win32 cleanups - nicer formula widget, better view switching - better file filter lookup - oops, min and max only worked for rectangular lists started 7.12.4 - cleaner Makefile.ams - transform was only working for [[real]] :-( (thanks Mikkel) started 7.12.3 - added right click / save for plot widgets - remove .svn dirs from dist started 7.12.2 - added support for TIFF predictor - added Tasks / Capture / Plot Bands started 7.12.1 9/5/07 - custom convolution of Plot no longer loses Plot wrapper - better plot colours - better spacing in plot status bar - added histogram differentiate, zero crossings - better ifthenelse on groups - added "expr.(expr)" form, removed builtin get_member - larger sensitive area for arrow crosshairs - added region-on-image-from-region, again - minpos/maxpos work for lists - Math / List works for Groups - maxpos/minpos return -1 for [] - max/min error for [] - stricter about the empty matrix being [[]] - image/Image ==/!= list was broken - empty groups were broken - remove special case for assemble on groups ... you now need to group->list first - [] as a group member means no-value - better Group insides started 7.12.0 28/4/07 - fix up 7.10 compat mode - more fixes to the convert.sed script - small fixes to 7.12 toolkits for test_toolkits.ws started 7.11.18 10/3/07 - added plotwindow, floatwindow - duplicate plot was broken - floating plots have stuff - gtkplotcanvas.c only swallows motion/buttonpress events it handles - gtkplotcanvas.c no longer tries to do focus handling - plotwindow status bar - added plotmodel.[hc], plotpresent.[hc] - plotview has a caption, displays class name - better captions for real/group/vector in heapmodel - gtk_plot_canvas_destroy() was not unreffing the pixmap (thanks Simon) - added next error stock item - better clock value display - added keep-child-windows-in-front pref (thanks Rachel) - gtkplotcanvas.c has new cursor handling stuff to help nip do cursor changes for middle-drag scrolling - lots of toolkit tweaks - revised the manual - ooop, increment_filename fix, it was putting the number at the start of the filename if there was no number there - better batch mode error messages, added -V flag for verbose messages - oop, variable name from filename was a bit broken - started revising the examples - increment on save and browse thumbnails were broken by gtk-2.10, gah - removed debugging menus - bump for 7.12! w00t started 7.11.17 26/1/07 - snap hdrag of columns to make lining up easier - better CSV import - added zero-excluding mean and deviation to Math - better set-workspace-name on ws load - started a ws background popup menu - grey ramp orientation swaps w/h - better display control bar scale/offset for HDR XYZ/Lab/etc. images - possible fix for intermittent fail to recomp on edit bug - fix for image * group - paste in gtkplot sources (we will probably need to hack it about a little) - added plot/plotview - oop, memleak in icontainer - added a temporary Plot menu for testing - set plot tick step to avoid mad mallocs on large ranges started 7.11.16 21/12/06 - look for release on rulers as well as press - adapt for new Hist system - use im_concurrency_set() - add im_get_option_group() - oop, recursive invocation gah - slightly better error messages - better mainw title bar text - added 'splits' - better trace / profile / leak options - more robust find chart calib - only interpret RGB16 for display for int formats - change im_histgr args - oops, paintbox could set delete-on-close sometimes - custom blur has many more controls - better inter-workspace "depends on ..." messages - chop/assemble image arrays now work on groups of groups, not list of lists so you can process the chopped up image - added 7.11 toolkit_tester, plus a little sed sscript to update old workspaces started 7.11.15 6/12/06 - tiny fixes to startup code started 7.11.14 6/12/06 - Vector arithmetic fix - Vector display class - more Matrix fixes started 7.11.12 8/9/06 - Image Rank no longer rounds up - only obey IM_CONCURRENCY pref in GUI mode - added GVALUE input/output args - added set_header, Set Metadata - use LC_ALL rather then LC_MESSAGES (thanks Simon) - better range == 0 check in conversionview - re-added make-named-column action - better textview reset during background recomp behaviour - added LUT from scatter - added AC_CHECK_TOOL to configure to find tools for cross-compilation. - added map_nary, Crop now loops on all args - test for glibtoolize during configure - added tag image as hist, set type, image->matrix more flexible - added get header field - added Real displayer - reordered Image menu - optionview refresh was a bit broken - ruler resize was a bit broken - map_nary recurses - make image windows children of the mainw ... so they can't pop behind - "mean" can do lists of images etc. - move ->parent from idialog into iwindow - revised to_list behaviour, added to_Group - show save prefs automatically on save - removed broken scroll on focus code in columnview - map_*ary no longer loop over lists ... they often represent compound objects, eg. "mean [1, 2, 3]" - new preferences viewer - kill parent of nested dialog now kills dialog as well - more JPEG save prefs - CSV save prefs started 7.11.11 18/7/06 - small polishes from gtkdisp3 - tweak for im_init_world() changes - better behaviour for scale == 0 in conversionview - better original-filename handling - use im_msb() for GREY16/RGB16 images - csv2vips wrapper update - added parse_time - fontname defaults to "Sans" ... stops a warning on load - set window title less often - update i18n infrastructure started 7.11.10 23/6/06 - sync CVS again - oop, selected closed columns caused kb grab confusion - added call8.[hc].. vips8 interface - new builtins vips_image_new, vips_call - reworked doc build again, seems to work in dapper now - added missing .br to man page - allow '_' in environment variables in "expand" - mainw tooltip reports operation cache size - upped default memoisation cache max to 10000 - better caption for Group objects - oop, mac os x detect was broken - GSL error handler - more work on vips8 interface - add gcc attributes for varargs and noreturn - mac build fixes - keep prefs in ~/Library on mac - vips8 interface done - more tweaking for vips_call for robustness - quick stab at background recomp in workspaces ... some stability problems - tiny Toolitem fixes - "Calculating ..." appears in status bar during a background recomp - is_image builtin says yes to vips8 images too - added Analyze and Vips8 menus - added vips8_get_header builtin - better ifthenelse behaviour for image/constant mixes started 7.11.9 15/5/06 - reverse order of decls in image_name etc. to sensibleify Change Header options - better HIST preserving - bug in complex display control bar (thanks Jean) - use gtk_disable_setlocale() to preserve LC_NUMERIC setting (thanks Peter) - more g_ascii_strtod() and friends for double parse/print - disallow vips funcs with no input args - gtksheet was freeing pixmaps with g_free(), not g_object_unref() - better matrix type guesser - optional link to vips8 for testing - CSV load/save - strict reduction of vips_call arguments prevents GC during argument gather and dangling pointers - oop, problem in Transform in Image.def - added "dir" builtin - always grab focus for bottom entry widget on column select - even more test view reset tweaks - added "objects in workspace" count to main status tooltip - limit number of cursor shape updates started 7.11.8 22/4/06 - added gravity, Find Projections now shows centre - added project - added OpenEXR read support - fewer int/void* tricks to help x64 - call im_existsf() more sanely - better Group caption - don't save/load ->name automatically ... better 7.10 compat - better pointer printing - added support for RGB16 and GREY16 image types - workspace window size is saved in ws file and overrides the global default - some edit dialogs now done with member automation - fixed NO_SPLASH - fixed scroll to row on error, for closed columns started 7.11.7 11/3/06 - allow complex constants of the form "12j", cf. python (also allow i) - optionally display complex as "x + yj" - ifdef'd out some more debugging code .. saved 30kb! - new Managed class abstracts out code for GC/C managed objects - Imageinfo now sits on this - Managedfile object replaces the thing we had for read - split trims trailing fails too - scrapped ELEMENT_IMAGE .. we just have managed objects now - added managedgobject - added experimental Clock class - moved some views into modelview.c - delay showing the hglass for 0.2s - change clock to seconds, subclass off Real - modelview now has a right-button menu - disable tile fade animation for thumbnails - model _get()/_set()/_load()/_save() now automated - sort column jump widget - better scroll-to-column behaviour - column jump is tear-off-able - column jump is sorted by column name and name length - added geometric mean - added sum, product - added linear regression - fixed assert( 0 ) for VIPS operations with an implicit DISPLAY param - added optional dependency on GSL, added gammq builtin - print_base was broken for some argument combinations - shift + mwheel scrolls left-right in workspaces - update thumbnail on falsecolour / type changes in display bar - matrix now uses member automation - main uses GOption command-line parser - make sure we don't clear dirty on rows coontain errors - optionview only rebuilds the menu on change - 7.10 compat defs updated - itextview/clock fixes to make editing members easier - added -o cmdline switch - scrapped print-last mechanism ... printing main from an associated .def file is much better - always set argv, allow save of Image, split save to file and print value - save Matrix as well with -o - added -e option - revised man page - added Find Projections - raised default memoisation cache size and heap size started 7.11.6 18/2/06 - custom morph was broken, grid was broken (thanks Dave) - Matrix_file now uses "search" - reorganised morph menu slightly - added Format / CSV import - better handling of display of very long strings - added lazy (read "filename") builtin - \n was missing in expr_info error report - removed "save successful" info box for great HIG-ness - added is_prefix, is_suffix, is_substr - Pathname widgets add to session path - widgets like Group, Toggle, etc no longer add annoying stuff to tooltips - readded "auto-recalc" menu item - renamed Slider as Scale and added a caption field - oops, string constant "\\" failed - drag from konqueror might work now - added hist_thresh, added threshold items to Image / Levels menu - prettier Scale display, better display of multiline class member formula - added correlate, correlate_fast, Filter / Correlate - added "jump to column" menu item, handy for navigation in large workspaces started 7.11.5 15/2/06 - added "search" builtin - (c) line changed - itextview mouse enter/exit now does help/highlight - itextview insensitive in noedit mode - tweak formula to stop resize with clearlooks theme started 7.11.4 18/11/05 - added tile fade pref for Kirk ... it is a bit slow on win32 - fix compiler crash for [1..2] (thanks Jay) - automatically add an icon to the win32 .exe - added -main_load_args switch ... just a temp hack - better -main printing - added "path_separator" ... either '/' or '\\' depending on platform - better char constant parsing - better char constant display - added path_relative/_absolute/_parse - prefs now switch between / and \ automatically started 7.11.3 26/9/05 - display original filename only for iimage which really were loaded from a file - ppm read uses direct open, rather than converting to vips - fixed used-before-set problem in option ... could cause segvs when displaying rows built from .defs containing errors - added Filter / Blend / Alpha Blend (thanks rich) - added Edit / Info (Ctrl-I) to mainw, some stuff there now, space for more to go in - removed nip2-7.11.ebuild now that nip2 is in gentoo portage - desktop integration binds .v files to nip2 (thanks Ruven) - align cols marks the ws as modified - configure.in magic stolen from pango ... we now automatically disable gobject cast checks in production builds - display formula rather than value if there's a visible graphic - falsecolour and type applied to thumbnails - fix gcc4 warnings - use g_mem_profile() - plot slice can now plot along any arrow - oop, some filters inadvertantly overrode width/height - fix isclass is_class confusion - itextview only shows value for non-class rows ... we assume members / graphic will show the value, so we show the formula - don't apply scale/offset displaybar controls to histograms / fourier images if Interpret Types is on - better initial kb focus for file load/save dialogs - better tooltip for iimageview - tooltips on demand function added - colourdisplay, ... adapted to new dynamic tooltip API - use input-only eventboxes for spinbuttons - better rowview tooltip - oops, buf_appendc() was a bit broken, fixed in vips too started 7.11 (1/6/05) - view image header now shows meta fields too - does INTVEC args to vips - now in CVS - added Analyze format - added name2gtype, gtype2name builtins - added get_header, grid, matrix lr/tb join - slice into tiles was broken for case tile size == image size - removed led.[hc], now uses stock system - always define our own strcasestr - icc import offers to use embedded profile - added 7.10 compat mode - "reset" in display bar resets to workspace default, not to 1/0 - column titlebars reorganised - reenable gtksheet support now gtkextra-2.0 is out - original-filename tracks name loaded at - bumped version to 7.11.2 - oop, fixed a nasty refresh bug - configure now adds LEXLIB to link line - pasted gtksheet code in so we can hack it - added matrix area selects - added Matrix / Extract / Area - slightly better default widget handliing in dialogs started 7.10.11 - docs no longer contain absolute nav links - increment filename on save works again - hide closed columns in NOEDIT mode - install a .desktop file for GNOME (thanks Denis) - group, right click, ungroup, no longer pops a spurious error dialog - new pref lets you not use the crosshair in image display windows ... some desktop themes have very annoying crosshair cursors - call libMagick less to reduce segvs from the file open dialog with broken ImageMagick libs - show args in vips history - fixed a rounding bug in image resize which sometimes made it miss by a pixel started 7.10.10 - fixed crash in vips_call with repeated calls to fns with large image vectors - fixed crash replacing a region with an image - fixed exit if temp area was missing (thanks Denis) started 7.10.9 - allow filenames containing ':' chars - uses im_render_fade() for prettier image display - added CCITTFAX4 compression mode for TIFF save - oops, image save options were being ignored :-( - fix 64-bit compiler warnings - fixed problem with very long filenames - recover-after-crash no longer messes up recent menu - "Jump to" in program window was broken - fixed a crash with very large objects (thanks David) - swap shift/control scroll modifiers to match HIG - rotate matrix works for any size matrix - rotate quadrants works for matrix as well as image - added recalc after reload start stuff - use auto label wrap - firefox is now the default HTML viewer on *nix - limit matrix display size to 10x10 unless we have gtksheet - small startup speedups - destroy views when a column is folded away to save some memory - added Image Rank - band join is much faster at joining many bands - safer empty temp area - added cute column open/close animation - fixed a couple of problems with region dragging in compatibility mode - removed column name dialog (thanks Joe) - program window now lets you collapse the current kit - allow \r in .def files (so we work with DOS edited files) - added Image / Tile / Chop Into Tiles - added 'Open Examples' started 7.10.8 - explicit gthread dependency makes us work even if vips is built without threads - tiny .def fixes - "-time_save" switch turns on image save timer for benchmarking - mainw, trace, imageview, program now use gtkaction / gtkuimanager - mainw and imagewindow 'view' settings now update prefs for you - trace window uses new gtk_text_buffer/view widget - image header uses GtkTextView for history display - program window uses GtkTextView / GtkTreeView - windows remember their size and pane positions - fix a crash for edit defs of live widgets in prog window - removed default file type pref ... now remembers last image and matrix file type - added "Set As Workspace Default" to conversion bar - mnemonics for more popups - fixed a crash if you quit mainw with an image window open (thanks Jay) - new default cursor for imageview windows (thanks Jay) - regions only update when grabbed - now passes distcheck - use g_setenv(), g_mkstemp(), IM_FREE*(), g_set_application_name(), g_set_prgname() - better cursor change layering - better initial toolkitbrowser size - absoluteize & canonicalize VIPSHOME (so we work with a relative path for VIPSHOME) - better time-to-go text - more fixes to help compiles on 64-bit systems - invalidate operation cache on paintbox actions ... makes paint on FFT work again - save settings in APPDIR on win32 rather than a dotfile - added SAVEDIR environment variable started 7.10.7 - tiny build fixes - added "_" builtin function for i18n of toolkit menus - softer 'unpainted' checkerboard (thanks Kirk) - added "-i18n" command-line switch ... outputs constant _() strings and quits - compiled string constants are automatically shared between all defs - (_ "kjh") substituted at compile time - started marking up .defs for i18n - trace_args() was printing backwards started 7.10.6 - uses fftw3 if available started 7.10.5 19 oct 04 - removed "beta" from version - perspective distort works on groups (thanks Joe) - added get_left/get_top - chaneg and tag colourspace now have option boxes - new enum class and option builder cleans up some stuff - fixed crash for display control bar scale on black image (thanks Mikkel) - fixed image windows appearing off screen for workspaces made on large displays being used on small displays (thanks Mikkel) - un-offset regions during create in compatibility mode (thanks Rachel) - fixed a race in vips history memo-isation (thanks Joe) - speedups to vips function call history too started 7.10.4 4 oct 04 - always include formatted html in dist, and install on install - enable line crawl on win32 - prefs now autolayout, so look sensible even with font changes - added Image/Bands/Extract|Insert|Delete - linear match now works for groups - prefs were not always saving automatically - white_balance now takes two args: uses band ratios in small image to fix large image ... makes using one image to fix a group of images much easier - also, works in XYZ, has adjustable white point target - new icon (also used in About) - LabQ and LabS conversions were broken - check for strcasestr in libc - display symbolic names in iimage caption - oops, custom rank menu item was broken - use a dumber sort algorithm for row recomp order ... glib was skipping too many tests - thumbnail convert settings update is smarter - much faster display convert bar - Joe's .defs are in - image blend is smarter about reordering args - small win32 fixes - oops, did not remove all temp files if debugging was turned off started 7.10.3 13 sep 04 - to_real now works for toggle & bool - Rubber menu items were b0rked (thanks Joe) - Rubber scale removed (rubber transforms now automatically rescale) - fixed crash if you gave a number as a superclass (thanks Joe) - win32: block log output to prevent annoying console window appearing - shared model for toolkits menu and toolkitbrowser for big speed-up on main window build and better update behaviour - tk browser columns are reorderable (thanks Joe) - better image type guessing (thanks Joe) - thumbnails now use Type hint (thanks Joe) - added simple number base conversion - compound unary ops on Rect now work on width/height rather than left/top ... so abs(Arrow) now gives the expected result - repaint regionview immediately for better feedback - group save can save unboxed images and will iterate over nested lists - added trace VIPS operations, including cache hits/misses - small menu polishing - better 'revert to defaults' in prefs (thanks Jay) - documentation revised - fixed dependency tracking inside zero-arg hidden classes (thanks Joe) - help system updated started 7.10.2 23 aug 04 - convert bar settings now affect thumbnails too (thanks John VanVliet) - show hglass cursor more often - free rotate is now -180 to 180 (thanks Jay) - added 'toolkit browser' - work on docs - small toolkit fixes - better mainw decoration handling - minor main window menu rearranging started 7.10.1 28 july 04 + top-bottom 2 point mos was broken (thanks Joe) + added aliases for resample and estpar to help compat mode rubber sheet + colour ops on LABQ were broken by the alpha channel stuff + larger default max heap size (thanks Mikkel) + added ebuild (thanks Ruven) + oop, fftw includes missing from IP_CFLAGS + added Expression widget + suppress unneeded textview in NOEDIT mode + better textview layout + indent subcolumns in NOEDIT mode + better regionview text layout + Image/Format reamed to Image/Number Format (thanks Joe) + prelight for rows, expressions and spinbuttons + better row label layout + menus reworked (again) for Expression class + oops, resample renamed as transform_search + win32 build fixes + added "Align Columns To Grid" + better focus handling in imageview (thanks Mikkel) started 7.10.0 21 june 04 + toolkit menu is now built dynamically from heap ... you can write functions that generate menus + menu items can have tooltips, icons and labels + reorganised all menus + added "scope" keyword ... helps remove "root" from toolkits and makes them relocatable + added "solarise" filter for fun + added "diffuse glow" filter for fun + slight improvement to references to non-local members in deeply nested classes + ungroup will now also unpack lists + fixed segv if option menus were rebuilt while posted + added vips function call cache ... memoisation! + added prefs for #cpus and #memoise + gah, had forgotten to add several new widgets to model_base_init(), so they were not loading correctly if used in prefs + put tearoffs back and fixed accels + added empty-temps yesno on startup + -main can output many images correctly + display control bar false colour works on RGB images + regionviews are offset in compatibility mode started 7.9.7 1 jun 04 + enable broken for gtk all the time in program.h (thanks Ruven) + now uses im_text() to paint paintbox text (imageinfo.c:2185) + better zooming from menu items (imageview.c, imagepresent.c, imagepresent.h) + extra recalc on startup to build classes before we load args (main.c) + set_output() was missing an "!" before imageinfo_file (graph.c) + saner startup error logging (main.c) + expr_error_print_all() now goes to a buffer (expr.[hc]) + program.c has an error lister + main.c knows about new error lister + prefs.ws had max_undo broken by precedence change + String now interprets and expands C escape chars (eg. \n) + fixed close program window with selected text crash + fixed edit of row with error itext crash + added tile and Tile (replicate/mirror ... using im_replicate) + leak testing only in DEBUG builds + binary and unary operators now track the function they were called from, for slightly better error messages + Number spots trailing characters + shadow and text paint work on labq + added resize longest axis mode to Shrink_to + fixed up menu dumper + don't track load/save progress in command-line mode (thanks Joe) + arithmetic and relational ops now work on images with a mixed number of bands + UI polish suggestions from Joe are in or noted on the TODO + better update of visible hints in imagedisplay + new menu item system with "action" member lets you have icons, mnenonics and i18n for toolkits + compatibility system for 7.8 workspaces + better recent menu started 7.9.6 8 mar 04 + added Group class + .def files rewritten for Groups, also many enhancements + oops, imageview=>file=>view header was broken + prefs option for 1 bit TIFF write + scale in display control bar was broken (thanks Ruven) + removed on-demand compile, caused strange recomp problems :-( + value display no longer decompiles class args + tracks lineno for tools + better "not defined" error message + better "bad parent class" error message + better error messages from calling VIPS functions + better "link report" error message + relaxed the restriction on superclasses ... you can now have anything as a superclass, including a full constructed multi-arg class + detects redefinition of syms within a single parse action + precedence change: "?" and "." now the same precedence, like C + better "member not found" error message + spots nested comments + visualise image can handle labq hist + case ignored for class names in ws save files + SHIFT-mwheel now zooms in and out, like gimp-2.0; CTRL-mwheel scrolls left-right + autosave does not back up system workspaces (eg. prefs) (thanks Joe) + raised arrow/region create threshold + optionmenu swapped for gtk-2.4's combobox + now gtk 2.4 only, gah! too annoying to have lots of ifdefs + uses GtkFileChooser + oops, String/Number did not implement load/save + much polishing + row.c no longer tries to recomp all rows that ref "this", was causing confusion ... so, much faster, but will change row recomp order in some cases + hmm, trace was a bit broken + auug, instanceof_exact was broken for deeply nested classes, must have been like that forever + fixed a nasty and long-standing bug with shared classes ... we now always copy code rather than trying to cache it + ruler menus now have mm and offset setting + got rid of all xoffset/yoffset stuff, what a pain it was + Rect (and hence Arrow, Point, etc.) now behaves (roughly) like a complex for arithmetic + better select behavior on thumbnail drag + renamed Point as Mark, Point is now a subclass that lets old nip workspaces load + added Fontname widget + colour picker can be pinned up + better image thumbnail in workspace sizing + renamed Filename as Pathname and added a caption + all menus items rewritten for new batch system started 7.9.5 6 feb 04 + Rotate_fixed now has an option menu for the angle + imagearray_chop was broken + image thumbnail drags no longer embed the workspace name (unless they have to) + merge workspace now shows an error dialog on failure + statusview does not display more than 8 bands + workspace saves view mode in files, and mainw knows about it + now uses pkg-config to find vips + splash does not focus "remove" toggle by default + oop, hourglass was broken + better hourglass animation + select/extend select now works on image thumbnails again + PRINT_LAST now done in workspace _dispose() + drag image thumbnail to background to make a new column and put in a link + compile now delayed until value needed ... saves 10% on startup time + value pointers only registered for GC if necessary .. saves another 3% started 7.9.4 9 dec 03 + removed last gtk_timeout*()s + fixed some memleaks + views now _sink() their child views, so even if views don't ever get added to containers (eg. toolview, rowview), they still get freed properly + niprc now sets gtk-can-change-accels ... no one will discover this otherwise + added Custom_blur, dropshadow now uses a gaussian blur + some better error messages + Toolkits=>New items for filename, number, string + usage and About have version info + row delete now asks for confirmation + faster and more comprehensive common subexpression removal ... removed the 'optimise' option, might as well have it on always + better session path behaviour + much better open recent menu + better graphic save/replace scheme ... image filenames now change in a much more sensible way + better iimage/iregion/iarrow caption scheme + can now drag from image thumbnails, cool drag icons + progress feedback on non-vips saves, and you can cancel too (!) + added extract_row, extract_column, extract_band, join_lr join_tb + extract_area now works for matricies too + better ruler tracking at high magnifications + defines HAVE_FFTW so we actually save and load wisdom now + "pos_changed" signal stops all the suprious imageinfo changed signals + "file_changed" signal lets iimage know when files are swapped about + better vips_call error messages + image cache is on mtime as well as filename, so loading changed files gets the new version + auto-reload on file change preference + reorganised main window menus and scrapped "Insert" + done a simple splash screen with some startup feedback started 7.9.3 20 oct 03 + better welcome message + command-line mode with -main/-script/-workspace/-benchmark flags + better middle drag scroll + fixed the annoying race condition in repaint + fixed annoying rounding problems with colour drag + added TRUE/FALSE as synonyms for true/false + better image zoom shortcuts + more HIG-y layout in NOEDIT mode + added String/Number types + windows have icons, yea + fixed browse icons window + message internationalisation done + en_GB translation file + more higgy Stringset class + better help system for dialog boxes + paintbox now has buttons for tool select + cursor shape change in subwindows + load and save accelerators + configurable accelerators on the toolkit menu + more HIGgy dialogs for sliders, regions and matricies + basic "Open Recent" thing on workspace file menu + better keyboard nav for workspaceview + reworked image viewer for more model-view-ness + unified image view state + hmm, row_new_heap had a double paste, wonder how long that's been there ... should be a bit faster at recomps started 7.9.2 (30 sep 03) + toolbar accelerators done + image display, colour display, region display all reworked for gtk2-ness started 7.9.1 + HIG-ified (I hope) + broken into SDI interface + prefs dialog + toolbar in mainw + progress dialogs for image load/save + new error message system + live watch system started 7.9.0 (1 aug 03) + compiles with gtk2! + lots of cleaning up started 7.8.11 (30 jul 03) + better run-nip.sh start script + tiny fix to Calibrate_chart, thanks haida + big LED stolen from mozilla, just one of them now + add "%s" to existsf() calls for filenames with % in, thanks Clare started 7.8.10 (22 may 03) + icc profile JPEG save option + "." now on end of datapath in prefs + fixed a race condition in regionview ... could crash during paint on slow machines + better vector/image ops + D65 <-> D50 conversions improved + added d50 macbeth data file + added "measure" to _stdenv + added "insert", "extract_area" to _stdenv + "recomb" now works on matrix, vector etc. + more use of extract_area + mark_tree() is now iterative, so no stack overflow on large heaps + reduce_spine() is (slightly) less stack-hungry + recomp_row() will not rebuild models for rows with errors ... is this the best way to stop it though? + calib_chart now works on 16 bit images + bumped version to 7.8.10 to match vips ... less confusing + changed doc builds so that we can include formatted documentation in the make dist + fix to drag-n-drop code on winders (thanks Jim) + fix to incorrect error message in file info view + blocked dash crawl on winders, does nothing but flicker + "config" help option (thanks Kirk) + columns resize on close + middle-drag in workspace scrolls + new version of Joe's x-ray stuff + spec/ now has RPM .spec file + added a "run-nip.sh" startup script + moved reload to program window + drag column and workspace gets a + cursor + added "Match" to Image menu + added "Tone_for_print" to Print menu + new macos icon, thanks denis started 7.8.7 (10 feb 03) + added set of relative constructors for point/region/arrow/etc. + used in various places to fix problems with dialogs on images with displaced origins + _vislevel member sets default visibility level + updated widget menu items to set vislevel + added has_member, get_member builtins + add "%s" to everror() calls so we can have "%" in error messages safely + better if-then-else overloading + add "." to the end of the default search path + added "Area", a non-resizeable region + option.c was not initing value edit correctly + better initial size for option edit dialog + added orderlist_scan() + better title for region edit dialog + better row_save_test() means member ordering does not get lost on clone of edited rows + imagedisplay_link() no longer makes conversion for you ... change to iimageview.c _init() in step (removes redundant create/destroy pair) + imageview_new_area() could pick shrink == -1 in some circumstances (thanks Joe) + drag file to imageview or thumbnail does replace-image + Region can now never fail (thanks Clare) + better feedback during paint image creation + better imageview File=>New=>* for images with displaced origins + added Edit_header to Image menu + catch errors from libxml2 + Image == Vector was broken + Overlay has a "lock size" toggle + added Image=>Insert + renamed "Clear edits" as "Reset" + saves/restores fftw wisdom for first fft speedup + snap-to-* on region drag could resize region for zoom != 1 + statusbar gave bad numbers for FFT and histogram images + use im_invfftr() for speedup + better ruler display at high magnification + oops, program=>find was broken + pin-up now part of dialog started 7.8.6 (23 dec 02) + swapped list for hash table in heap set of managed pointers ... startup time fallen from 2.4s to 1s! + much better heap-full reporting + knows about png + patch from Hans Breuer: + paintbox sort-of hacked back in + fixes to file selector on win32 + misc. win32 #include fixes + OK buttons in dialogs now verbs + knows about magick + oops, Colour_chart_from_matrix and New_CRT_test_chart were broken in 7.8.5 + new heap_is*() function style handles eval errors better + paintbox rewritten ... now paint bar + snap to guide added + much smarter region repaint system now does true xor animation + dash lines crawl in the background + Rect/Region/Arrow etc. can now have float args and won't barf on images with strange offsets + default image file format preference + colour temperature conversions sorted out (thanks Haida) + fourier transforms now work with optical transform, rather than having optical built into visualisation (helps paintbox) + removed use of g_mem_chunk() + new colourdisplay class for displaying swatches of flat colour + drag-n-drop colours + tries to detect C stack overflow in reduce_spine() + new trace system reduces C stack usage during reduction + fixed a few memleaks + copy-on-write for member edit slightly reduces overcomputations and makes changes feed forward more gracefully + scale column coordinates with changes in font size + Tilt_brightness now works with n-band images + nativize paths + drag URIs to main window to load files (thanks Hans) + load any file type from command line arguments + small menu fixes + added RPM .spec files to distribution + help launcher for mac os x started 7.8.5 (12 nov 02) + added a bunch of "%s" to allow percent in tooltips + rearranged reduce_spine() to trim stack usage ... should reduce C stack overflow segv on deep recursion + added IR sample images to data dir + added rachel.con IR sharpen matrix + added Join.Array to build an image array + added Rubber stuff to Image + removed auto column switch on row select + added New to imageview window + finished-ish docs + >3 band images now display as RGB, 2 band images as mono ... helps display of imported RGBA/GA tiffs + better update of toolkit menus on tool change with zero-param classes + better positioning in toolkit menus with hidden items + default vid crop fixed + added Overlay to Image menu (thanks Joe) + added Calibrate_chart and Calibrate_image to Capture menu (thanks Joe) + help buttons linked to html manual display + can now load workspaces from win machines on *nix, and vice versa (tries both types of dir separator) + added Joe's Xray menu + present menus and rows in definition order rather than reference dependence order + added Browse_multiband (thanks Joe) + can now pop up help viewer on win32 as well + knows about new im_LabS2Lab() and im_Lab2LabS() funcs + junk Hist on load to lessen balance confusion + more helpful save/replace file dialog titles + longer doubleclick time + sub-menus tear-offable + settable default image window size in prefs + optional auto-popup of new image rows + gtkfilesel2 knows not to select something twice + larger default max heap size + ws save files are prettyprinted and uncompressed by default for greater portability started 7.8.4 (8 nov 02) + fixed recover workspaces (thanks Joe) started 7.8.3 (31 oct 02) + acinclude.m4 fixes for mac os x + set extension on get_filename if none set and not showing All + added Mosaic_force ... no tie-point refining, ever + only save edited sub-trees on workspace save ... shrinks ws files to about 1/3 their previous size + setlocale for numeric conversions to "C" to avoid "," as decimal point madness + escape C sequences in filenames (eg. "\n" etc) + vips functions and builtins now linked via main symbol table, rather than an extra lookup on "undefined" + pseudo-toolkits group VIPS packages and builtins + display help text on pseudo-tools in program window + "go to def" for program window + auto-expand for rows in program window started 7.8.2 (27 oct 02) + set $HOME on win32 + WinMain on win32 for non-cmdline start + -mwindows flag to stop command.com starting for non-command line start on w32 + lots of hacking on gtkfilesel2 for win32 compat + Matrix_file "" + New_mark.Region etc. menu item + more robust row recalc on .def edit + zero-arg local classes of classes sometimes recomped in the wrong order (thanks Joe) started 7.8.1 (18 oct 02) + d'oh, matrix constructors have to be classes for is_instanceof to work + much better change/refresh/scan behaviour for gtk_sheet + uses IM_DIR_SEP* for some win compat + many configure fixes for mingw + use gtk_fixed for workspace layout for gtkwin compat + rename Text -> iText to stop windows breakage + woohoo, fixed the grab problem in regionview + more robust workspace load + polishing started 7.7.23 (23 aug 02) + bug in history tracking + better filename select + OK buttons in multi-select fsbs turn on and off + supress "super" iimages for region/arrow displays + rulers and status bar know about Xoffset/Yoffset + regionview uses IMAGE cods, converts to model cods and back on refresh/update + defs adapted to origin stuff ... including Mosaic! + region create is ctrl-left + save-as-TIFF traps errors + done Plot and Resize, phew ... all menus finished (the ones I did anyway) + added namespaces to XML save file, prettyprint disabled, compression on + tooltips for toolkit menu items + "Name param1 param1: " string automatically prepended to help text + preferences for mainw start window size + menus reorganised to be more logical (I hope) + Separator class for submenus + column save adds enclosing workspace + drag in program window was broken + #dialog back in again, with an edit dialog + "menu item from column" thingy + refcount bug for long image load fixed + iDialog can autopopdown for represented obj destroy + toggle MB free/cells free + use gtk_sheet for text matrix display + configure detects gtk+extra for gtk_sheet + iimage caption displays name of most derived class + relaesed as 7.8.0 ! yea! started 7.7.22 (15 july 02) + started Print menu + added "expand" builtin ... expands environment variables in a string + filesel history fixed + reconstruction from overridden constructor in oo removed ... now just there for edits + done Colour menu, started Morphology + done Morphology menu, started Filter + if_then_else is now an overrideable binop + use (double) for image size calc to avoid int overflow + logical_and and logical_or can be overloaded ... still shortcut for plain types, so not quite like other overloads + done Filter menu, started Freqfilter (will become part of Fourier) + done freqfilter, started Histogram + sliders no longer each have a continuous member ... set with a watch directly from prefs + histogram visualisation + better trace will never evaluate graph unexpectedly + Real widget ... just draws a real number + better row name set system gets less confused + can now edit superclass constructors + better recovery after error in row recomp + better region caption + better scroll to new object for main window + "<", "<=" work on strings + started Image menu + small fixes for large files + image window title bar update fixes + auto select 1st matching file on load if no file specified + rename Patch -> Colour to fix class name / gtk type name confusion + classmodel_class_instance_new() now uses CLASS_new in preference, if defined ... lets you have separate behaviours for _type object creation and OK in edit dialog + Xoffset/Yoffset added to header view + default class == thing, class != thing operations in _Object + class params no longer have subcolumns ... stops O(n**2) increase in complexity with workspace size! + multiple select for for fileselect ... load many images/matricies/etc at once + on load, objects renamed to the filename they were stored in + better workspace scroll on new object started 7.7.21 (21 june 02) + override Pixel constructor in Colour and Generate_colour.widget + rename ... ivector -> iarrow + new op type for colour-through-image operations + better expr->err update on link clean + convolution matrix display now shows scale & offset + Matrix is now the base class, Matrix_vips etc. inherit from that + tags now decompile for better error messages + better graphic rebuilds for sub/super classes + better member-not-found error message + better new column positioning + rotate menu started + convert menu started + segv on CTRL-S on local objects fixed + flash help on row buttons + suppress display of superclasses with a leading '_' + better auto new workspace name + better column rename on ws merge + better scroll-to-visible for columns + row just uses "name" property now ... no "sym" + toolkit list now scrolls down RHS of main window ... no more resize probs + parent/child relationships shown with colour changes in rowview + removing column with an error resets error state properly + x2 speed up for recalc with fancy heap node serial number system, heh + better regionview create/destroy/link fixes occasional bad casts + better auto workspace scroll on load + scrapped .hd/.tl etc., too hard to overload ... builtins now + '' chars are now unsigned, signed chars are numbers in [-128, 127], chars default to unsigned (now unlike int, short) + regions/arrows/etc. now defined on Image, not image + better trace system does not confusingly interleave prints + small filesel fixes + Complex, List, Fourier menus + display control bar knows about fourier images + display control bar menu resets properly + bits of Arithmetic broken out into Log and Trig menus + Filename widget ... should help make an ICC profile chooser started 7.7.20 (17 may 02) + redone configure system ... data files now go in share/nip, not share/vips/nip + fixes to Pixel class and Generate menu + -image is now *-1, not im_invert() + separate '!' and '-' operators for better C-style semantics + better toggle/extend select for thumbnails + Yxy display + ops on Matrix class done + stats menu added + removed matrix size limit + errors -> ierrors to please mac os + keep local edits on reload + oops, classes as parameters were broken + member edit of local classes was broken + class arg checks inherited + view header dialog in imageview + colour menu + nasty bug killed for discovered dynamic references to dirty symbols + Colour widget shows a swatch and lets you gtkcolorsel for edit + rowview menu on subrows too, plus select/extend-select + ceil/floor added as builtins + lots of small polishes started 7.7.19 (10 apr 02) + it's now (c) 2002 :) + better LED spacing + "stop" sign toned down + split Expr to static stuff (Compile: parse/compile logic) and dynamic stuff (called Expr still ... reduce stuff) + Exprs can share Compiles if we know they will have the same code + copy-on-write for edits + 100s of times faster for large workspaces: load ws with 270 images = 7s + oops, temp files now unlinked properly + icon browser refreshes in idle handler, plus better cancel behaviour + destroy callback added to iDialog, popdown_cb memleaks plugged + memleak in model rewrite plugged + all class instances in hierarchy have the same "this" ... simplifies OO stuff a lot + removed heap_gc() from REDUCE_CATCH_START() for big speed up (d'oh) + smarter row dependency finder + leak plugged in get_image_info, plus more informative + reset menu item on graphic edit objects + ooop, added '\'' as a constant + C-style hex constants, better real constants + even fancier operator overloading scheme does builtins too, and is extensible for other user funcs + abs/max/min/etc. can be overloaded + lots of menus done! + newimage dialog removed + classes with supers don't display as pull-rights in toolkits + updated vips.m4 for IRIX + top level dirties now say what they're blocked on in tooltip + cast to int type now behaves as C (no more round to nearest) + reload toolkit works better + smarter image cache dependency tracking fixes occasional segv + _animate() in class build for greater interruptibility + "++" is lazier for list args + image ++ [] allowed + builtins can be overloaded + tidies to reduce/action + dmalloc support + better column/row select behaviour + better event handling in image windows + scroll wheel in image windows + class typecheck delayed until first reference for great speedup + lots of polishing + Mac OS X fixes: - change include order in ip.h for mac os x - test for mount.h, util.c, ip.h changes for space free display on mac os x - file size stuff changes - small include order changes - temp_name() fixes for duff mkstemp() - ignore GDK warnings (eg. locale not known) + changeable max print length, dynamic buffers + Pixel[] class + ontop no longer saved for workspaces + text values display left justified started 7.7.18 (1 mar 02) + load images from command line + new operator overloading system + new check system allows check to be inherited + nasty ii_destroy bug fixed + new trace option for builtin functions + nasty row destroy bug nailed + much better busy/not busy handling, feels smoother + more sensible workspace checkmarking, good speed improvement + more info displayed in image status bar + red error arrow not always unset ... eg failed file load + try to load a damaged (eg. truncated) image file ... wrong err msg + recover ws after crash fixed started 7.7.17 (23 jan 02) + changed appearance order for subcolumn + params and super start with vislevel 0 + '.' now binds more tightly than '\' + '\' renamed to '?' + '&&' and '||' split to separate logical and bitwise operators + removed local function display + better code generated for access to members across nested classes + better preservation of sharing in class browser + decompile makes loop labels + non-row locals link back to enclosing row correctly + inter-row dependencies via non-row locals spotted + user def of default constructor banned + nested classes with implicits refs now work + operator overloading added started 7.7.16 (14 dec 01) + delay GC to once per sec where possible + ruler preferences + rows only reset on enter, not on dirty + graph.c indents prettily for easier debugging + nasty GC bug nailed + trace prefs options + assert() on program forced close fixed, class redef bug fixed, program window tracks filemodel->modified more closely + region clone menu + destroy regionviews on hide + smarter and simpler layout resize + final (I hope) precedence changes ... now just like C + '<<', '>>', '~' and '@' (function compose) added + row recomp refinement ... simpler and faster + oops, menu items all done in imageview + row locals with external refs were not adding to top level dirties correctly + more rigorous backtracking for deducing recomp order + fancy pantsy heapmodel_reset() system for great justice + traced and optimised recomp ... seldom repeats itself now started 7.7.15 (16/11/01) + rename workspace on top level load + added workspace merge + added column merge + clone stuff done + layout sizing done (tho' not very well) + toggle select and range select for rows + junked all old menus (now in scraps) + fancy new view manager only creates views when required ... x2 speed up on workspace load + file browser lets you change the suffix by typing (eg. type "fred.jpg" into save box while files-of-type is VIPS and you save JPEG) + replace and save matrix and image graphics + new Matrix class hierarchy + .nip-x.x.x directory stuff added, "Preferences" workspace loaded on startup + Watch class for getting pref settings quickly in C + removed all .iprc code + region drag now synchronous, so it can't lag + max heap size scales with workspaces loaded + duplicates automatically removed from paths, system files renamed to user directory on auto load + workspaces reorder correctly + new row number layout scheme using on model pos layout + row drag 'n drop reordering + is_class predicate + Abut.Left_right and Add menu items done as trials + syntax changes to become more C-like: and/or/eor/not keywords removed ... now &&/& ||/| ^ ! & (join) becomes ++ and does list cat too ! (region extract) removed ^ (raise to power) becomes ** + precedences changed to be more C-like ... `\` now binds like array subscript started 7.7.14 + oop, about copyright line was wrong + model now has child_add(), child_remove() methods + child_add() child_remove() used for much init and cleanup ... nice! + fewer typed parent/child pointers in models ... getters to cast model parent/child instead + parent_add(), parent_remove() methods in model + XML prettifier does indenting in save files + load/save moved to model from filemodel + text now loaded too, new rhs child add system + forward references in workspace load now work + simplified _build_display() system with _link() method for view subclasses + new iregiongroupview class for managing sets of region displays + more intelligent naming of objects across workspaces + workspace modified set for more actions ... reflected in mainw titlebar + context pointers are back, but inited from _child/parent_add() system + split to nip package + reworks for new package structure + row_recomp() sorts regeneration by row depth + new scan/reset system + Text now derives from Heapmodel, scrapped the last of the model_link() funcs + _refresh_value() -> _update_model()/_update_heap() pair, with ->modified to control behaviour + _stdenv.def changes ... added is_space, split, splitl, split_lines, parse_pint, parse_int, parse_float + program window parses on popdown + gtkutil has set-2-adjustments-at-once convenience function + all tally models (subcolumn downwards) now derive from Heapmodel + Heapmodel -> Heapmodel/Classmodel + all widget models derive from Classmodel + Text now delays parse/compile until recomp + regenerate system now uniform between graphic and text representations + new model_freeze()/model_thaw() system to reduce model_changed() emissions + XML load/save done for all class widgets + only save edited formula ... deduce others + better target symbol naming for region/point/vector/guide create + dialog boxes now have GNOME2 button ordering ... F1 binds to help + old row_change() mechanism ditched ... much simpler and clearer now + all class.c getters renamed + _update_model() -> _update_model()/_new_heap() pair ... faster + ditched base/derived instance vars, new rebuild from base system from new unified model recomp system + ditched remake-from-base system :-( can no longer do islider ... but much cleaner and more intuitive behaviour + tslider is now a proper widget + better jumping region labels during scroll + switch current column on row select + region labels / image window titles change helpfully on workspace switch + fantastically more complicated row_recomp() now deduces recomp order from dependencies + graphic displays only save and restore their settings if they've been edited + text edit resets edits on sub rows + don't make a display or RHS for system rows (eg. this, check, name) + mainw_countdown_animate() now updates display again ... this may cause problems, have to see :-( + tslider has elaborate workaround for slider destroy during changed callback problems started 7.7.12 + added program window + reworked TODO list ... only 140 issues outstanding ... :-( + toolkitgroup now emits "changed" on any tool/toolkit change + find/find-next thing for program + new info mechanism + link report finds undefined symbols + tree view maintains sort order + model_child_add_before() to aid drag and drop reordering + toolview does menu reordering + popups pass down host widget + general "are you sure you want to remove" for models + destroying a tool now destroys associated symbol too + destroying a toolkit destroys all contained tools + destroying a top level row destroys the symbol + symbol/filemodel/model destroy split to finalize as well + stable owns a ref to syms it holds + if destroy a sym, mark all parents as having "not defined" errors + expr_error_set() now zaps compiled code to force recompile ... ensures user fixes problems properly + textview always recompiles lines which you hit return on + better typecheck error messages for widget classes + right button menu on rulers + xml save + load_text and save_text methods in filemodel.c for tool/toolkit load/save started 7.7.11 + added trace window started 7.7.10 + BI_CONS is lazier and faster started 7.7.9 + "print" builtin added + oops, parse_function() was not passing sym down + better checkargs function + x-ray print menu patched, duh started 7.7.8 + browse now uses new image display code + ooops, PPM/PGM/PBM read added + conversion is now refcounted + all old image/region code removed + old window/dialog code removed + paintbox/edit/magic/menu/calibrate/cursor/request/dragdrop also gone for now + last of X11/Motif gone ... # of lines down 20k! + iregion/iregionview added + finally GNUified it + fixed newimage dialog + region redone as subclass of image + ip class names now have initial caps + better iwindow popdown behavious + imagedisplay implements gtk focus model + imageview key navigation: left-right-up-down-in-out, zoom to fit + imagedisplay repaint probs fixed + new expr_value_new()/_destroy() system to track images + regionview added + cursor manager added to iWindow + jumping region labels! + nasty reduce bug nailed ... heap corrupted if super-class constructor failed + class construction errors handled gracefully + rubberbanding regions on imagepresent + point and vector display types added + ivector/ivectorview added + instance vars can be virtualised by heapmodel ... for code sharing between iregion/ivector/etc. ... sort of a lame MI fudge + regionview morphs between display types if unfrozen + Region/Vector/Point/HGuide/VGuide classes added + lists/image-bands index from zero + mark spine stack on GC ... oops, sometimes broke for nested recomp + reduce.c -> reduce.c/action.c + new action_strict() interface handles nested reduce_spine() calls correctly ... allows mutually recursive locals + some reworking of reduce.c ... still not very pretty :-( started 7.7.7 + [] can have whitespace between the [s + conversion.c added ... manages display conversion model and region/thread display stuff + _list.def and _stdenv.def reworked from Miranda 2 stdenv: foldl function args reversed swap renamed as converse foldl1, foldr1, map2, merge, replicate, scan, until added faster sort (merge sort) + option/optionview pasted back in + image/option parts of sym->recomp scrapped + tslider widget ... entry, plus slider + conversionview ... display control bar + tslider does non-linear sliders + now uses 100%, 25%, 400% etc. to show magnification + oops, mono to labq was broken + statusview.[hc] added ... status bar! + iimage now tracks derived image value as well + better file_info display for JPEG/TIFF/PPM in file load + iimage now just has vips_image as class param + matrix/matrixview added, old mask stuff removed + lots of memory leaks removed (thank you memprof) + workspacegroup is a symbol ... workspaces are named root.Workspaces.blah + matrix resize + matrix load + is_string now defined in _stdenv.def, rather than being built in + vips_call knows about new matrix representation + better scanning system for text widgets + better uop/bop error messages with text_decompile() started 7.7.6 + decompile for parameter edit, value displays parameters (tho not secrets) + save/save as/close added to model + new workspace save done + better notebook tabs + new iWindowSusp stuff now allows composition of window funcs + iDialog now allows multiple OK buttons + Save/Don't save/Cancel on filemodel close + nasty nested iDialog problem found and fixed + close all filemodels on quit + tookit.c -> tool/toolview/toolkit/toolkitview; toolkits are filemodels + all sprintf()s gone + empty/load/replace for filemodel done + workspacegroup/workspacegroupview added + toolkitgroup/toolkitgroupview added + model -> view links removed, signals for 'changed' ... bit simpler n nicer + views track parents and children + scan set for auto re-reads of widgets + reset/scrollto now signals too + now called ip2 + gtkdisp imagedisplay/present/asynch code pasted in + "image" builtin renamed as "vips_image" + image class added + iimage/iimageview added ... thumbnail display! + new (smarter) behaviour for spin expand/shrink; affects rhsview visibility as well as subcolumnview visibility + threaded display code patched in + imageview added + image display rulers, magnification, titlebar wired up to menus started 7.7.5 + better centering of dialogs over their parents + oops, silly bug in stable_resolve() + new expr_resolve() sorts out static/dynamic scoping problems + uses mkstemp() for temp image file names + new mark dirty scheme + small destroy bugfixes + better tallyrow_recomp_rethink() code finds the right expr more often + better binding to root for dynamic exprs + expr_resolve() before expr_check() + "super" member is a regular member, not a parameter + about dialog, with easter egg :-) + new code for recomputation of superclasses ... does "this.x" if any supers change, tracks use of params in super construct + warp focus to column bottom on column select + ':' char banned in file names + workspace load/save/save as/close done + workspace tab menu and tooltip + don't mark zombies dirty clean up of front end started + Symbol extends GtkObject, Workspace extends Symbol + Columnset renamed Workspaceview, members moved between it and Workspace + Column split into Column and Columnview + refresh_note() system added + Model class underpins symbol/workspace/column etc. + tallycolumn -> subcolumn/subcolumnview + tallyrow -> row/rowview + tallyitem -> view + tallyrhs -> rhs/rhsview + text -> text/textview + Heapmodel class added to underpin slider/toggle/option/matpanel + slider -> slider/sliderview + toggle -> toggle/toggleview + mono <-> sRGB gammas both ways now started 7.7.4 + reload $VIPSHOME/lib on menu and plugin reload + no longer includes gtkintl.h + better namecaption API + better iwindow/idialog/namecaption build inheritance + cleaned up naming in main.c + gtkfilesel2 now inherits from idialog + filesel now inherits from gtkfilesel2 + browse now inherits from idialog + now builds cleanly on Sun cc + found horrible gtkfilesel2 bug + fileselect removed + toggle/option/matpanel edit uses idialog + secret optimisation supressed for tally display + edit value (rather than source) for class params + edit reset on column after ENTER + asynch/menu bug fixes backported + Histogram.def renaming + -,/,* for realvec started 7.7.3 + secret now in terms of expr + compile now in terms of expr, not sym + bulletproof errors()/verrors() + resolve_names now knows about tally scopes as well as symbol scopes + linked global recompute and tallyrow recompute up + new link object joins up topsyms for recomputation ... saves a search on tallyrow dirty, makes multiple external refs work + better slider edit dialog + new code for '.' operator now records context in heap, so we can spot dynamic dependencies + improved link objects ... better handling of multiple links, more stuff deduced, support for static and dynamic links + dynamic dependency management + class parameter edit + new spin widget for class display open/close + class member visibility table, controlled by spin widget + new im_vipshome() startup code started 7.7.2 + ws error button colour fix + big sym/expr/row relationship reorganisation + better error handling + better tally tooltips + better toolkit flash help, plus flash for sub menus + fixed some input_push/pop() problems + reorganised main menus, better pull-right display rules + automatic.c -> mainw.c ... lots of renaming and tidying up + multiple workspaces linked to symbols, columnsets now make syms local to their workspaces + iwindow/idialog improvements, newcolumn/workspace dialog is now subclassed off idialog + countdown fixes + another nasty tallyrow destroy bug + error message display fixes started 7.7.1 ... 19/10/00 + another nasty destroy bug + class browser looped for some classes containing errors + default constructor now not displayed (unless overridden in an enclosing class) + recomp inside a class instance done + abs in _stdenv.def failed for complex + nasty gcc error in class_member() with -O2 + tallytext rhs handling broken out into tallyrhs ... tallytext is simpler ... now have graphic/klass/text display + new tallyitem_trigger system with better error propogation + fold/unfold button for class instances + button tooltip displays long messages + tally => row rename + recomp/refresh/refresh_value sequences optimised + expr_clone() now works for function members of classes + refresh_value no longer uses _trigger() propogation mechanism + now tracks prhstext ... everything but the function name ... needed for class edit of local functions + class/super now properties of expr, not sym ... classes are expressions, not symbols + displays args to function members of classes + sym_tab tracks insert order, used to order class members in tally + table_find_child handles hidden children started 7.7.0 + default constructors + escape cancel in idialog + 'root.' and 'workspace.' static scope references + recalc dynamic dependencies on link + super-class constructors are blocked from referring to locals (other than params) + class_member_base() stuff sorted out in slider.c ... usually need non-overridden value + display update block mechanism for tally stops widgets updating themselves + slider text value display redone + fixed a couple of nasty destroy bugs + 'Arithmetic.Add' now does sliders! + released as 7.7.0 ================================================ FILE: Makefile.am ================================================ SUBDIRS = \ src \ test \ man \ share \ po EXTRA_DIST = \ autogen.sh \ doc \ proj \ m4 \ screenshot.png \ nip2.desktop.in \ nip2.xml \ nip2.appdata.xml nip2appdir = $(datadir)/applications nip2mimedir = $(datadir)/mime/packages nip2appdatadir = $(datadir)/appdata nip2app_DATA = nip2.desktop nip2mime_DATA = nip2.xml nip2appdata_DATA = nip2.appdata.xml install-exec-hook: -rm -rf ${DESTDIR}$(datadir)/doc/nip2 $(mkinstalldirs) ${DESTDIR}$(datadir)/doc/nip2 -cp -r ${top_srcdir}/doc/html ${top_srcdir}/doc/pdf ${DESTDIR}$(datadir)/doc/nip2 -rm -rf ${DESTDIR}$(datadir)/doc/nip2/html/CVS -rm -rf ${DESTDIR}$(datadir)/doc/nip2/pdf/CVS if UPDATE_DESKTOP install-data-hook: -$(UPDATE_MIME_DATABASE) ${DESTDIR}$(datadir)/mime -$(UPDATE_DESKTOP_DATABASE) ${DESTDIR}$(datadir)/applications endif dist-hook: # make sure we don't get any .svn dirs from EXTRA_DIST # also "fred" gets left around occasionally -find $(distdir) -name .svn -exec rm -rf {} \; -find $(distdir) -name fred -exec rm {} \; uninstall-hook: # make sure we have write permission for 'rm' -chmod -R u+w ${DESTDIR}$(datadir)/doc/nip2 -rm -rf ${DESTDIR}$(datadir)/doc/nip2 -$(UPDATE_MIME_DATABASE) ${DESTDIR}$(datadir)/mime -$(UPDATE_DESKTOP_DATABASE) ${DESTDIR}$(datadir)/applications ================================================ FILE: NEWS ================================================ ================================================ FILE: README.md ================================================ # nip2 --- a user interface for libvips We now have a first public test release of nip4, a rewrite of nip2 for the gtk4 UI toolkit. If you have some time to try it, any feedback would be very welcome: https://github.com/jcupitt/nip4/releases ## nip2 nip2 is a GUI for the [libvips image processing library](https://libvips.github.io/libvips). It's a little like a spreadsheet: you create a set of formula connecting your objects together, and on a change nip2 will recalculate. This makes it convenient for developing image processing systems since you can watch pixels change as you adjust your equations. Because nip2 uses libvips as the image processing engine it can handle very large images and only needs a little memory. It scales to fairly complex workflows: I've used it to develop systems with more than 10,000 cells, analyzing images of many tens of gigabytes. It has a batch mode, so you can run any image processing system you develop from the command-line and without a GUI. ![image](https://github.com/user-attachments/assets/aa2c3e0f-9f96-4594-9f1d-62ef770d0775) ## Installing You can probably install nip2 via your package manager. For Windows and OS X, you can download a binary from the [nip2 releases page](https://github.com/libvips/nip2/releases). If you have to build from source, see the section below. ## Documentation nip2 comes with a 50-page manual --- press F1 or Help / Contents in the program to view it. ## Building nip2 from source In the nip2 directory you should just be able to do the usual: ``` ./configure make sudo make install ``` By default this will install files to `/usr/local`. Check the summary at the end of `configure` and make sure you have all of the features you want. If you downloaded from GIT you'll need: ``` ./autogen.sh ``` first to build the configure system. nip2 needs vips, gtk2 and libxml2 at runtime and flex/bison at compile time. If you have fftw3, gsl, goffice, libgvc you get extra features. ### snapcraft Rebuild snap with: ``` snapcraft cleanbuild ``` Though it's done automatically on a push. ================================================ FILE: THANKS ================================================ nip THANKS file nip is a rewrite of ip, so see the THANKS file for that package. We've had very helpful funding from the European Commission and from Hewlett-Packard. ================================================ FILE: TODO ================================================ - add tests for new stuff - seeing occasional "rewind image with active regions" message - should parse_float / _int etc. allow leading and trailing spaces? (split is_space x)?0 - how about adding zip2 10 [1..5] == [[10, 1], [10, 2], [10, 3], .. should be harmless, and quite useful same for zip3 etc. as well check zip use, we probably have this code there already, in various places - sharpen should use new interface? - can we call affine from nip2 vips_call? do we need a double array? - hough_circle etc. don't get cached ... they use the vips8 API and the vips cache only works for vips8 we can't turn on the vips8 cache since it does not know about invalidate - columns can move about on load in large workspaces - hide tabs if only one tab in window, though we'd need to allow tab drop anywhere in window for that - Matrix / New / Laplacian edit a cell, turns into a plain matrix need to override edit method same for gaussian rather tricky, compared to square / circle etc. - breadcrumb trail for prog window, so you can get back to where you were? - lambdas don't pattern match? (\[a, b] a + b) - load jpg, ^Q, no leaks load jpg, paint bar, paint one dot, ^Q, no leaks load jpg, extract area, paint bar, paint one dot, ^Q, leaks load jpg, extract area, paint bar, paint one dot, ^Z, ^Q, leaks load jpg, extract area, paint bar, paint one dot, close window, ^Q, leaks seems to leak: original image, one large regions on that image (full width) extract area operation extracted image region a bit bigger than paint action on that image 0) VipsRegion (0x8b052a0) VipsRegion (object), base class VipsRegion: 0x8b052a0, im = 0x887aad0, left = 192, top = 128, width = 45, height = 2 VipsRegion (0x8b052a0) 1) VipsImage (0x887aad0) VipsImage (image), image class 237x202 uchar, 3 bands, srgb, partial VipsImage (0x887aad0) 2) VipsImage (0x8016b30) VipsImage (image), image class 972x1296 uchar, 3 bands, srgb, openin VipsImage (0x8016b30) 3) VipsRegion (0x8b05340) VipsRegion (object), base class VipsRegion: 0x8b05340, im = 0x8016b30, left = 0, top = 30, width = 972, height = 283 VipsRegion (0x8b05340) 4) VipsExtractArea (0x882a910) VipsExtractArea (extract_area), extract an area from an image - extract_area ((VipsImage*) 0x8016b30) 142 158 237 202 VipsExtractArea (0x882a910) something to do with vips_image_wio_input() and the way it rewinds a PARTIAL image? called from im_rwcheck() - os x build reports missing jasper dylib? - draw_circle could extract, draw and insert for huge memuse reduction - section on compat mode for the docs see mail to MvGulick for some notes - expose more of the tone funcs in nip2 - quite a few hist operations have no GUI ... histspec, for example? - nip2 should use zooming support, if possible - the windows setup .exe install a bazillion .png icons we will never use, then installs them all again as .svg, which we will certainly never use - add extract_volume, cf. "grid" record tile_size in meta somewhere? grid could set it, save a parameter off extract_volume also extract_sequence to get volume over time - image + complex constant would be nice - ban parameters which are built-ins, eg. "im", "re" etc., you get nasty crashes see also notes below: a new parser could fix this can only ban for new code? do we have duff code in compat? argh yes there are at least 15 like this, fix them or fix the parser? also need to disable this check for compat defs better to fix the parser, it can't be that hard need to fix up the list comp compiler too, sigh - we can't define local superclasses at the moment eg. consider: Fred = class { Jim a = class { value = a + 12; } Jennie x = class Jim x { value = 99; } } you can't do (Fred.Jennie 12) since Jim will have a secret 'this' param (because it is a class member) and superclass constructors can't have secrets don't automatically give all members 'this' as a secret, check that they make references to other class members that do need secrets first - turn on GM in prefs, have to restart before _stdenv.def:magick sees the change - try this: Workspaces.untitled has_member "A1" A1 doesn't seem to work? - matrix_gaussian_blur could have an 'accuracy' or 'max error' param? expose in custom blur etc. - oo_binary etc. needs revising, we don't search down branches as we should for example, Matrix does: // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], which is stupid, we should not wire Real and Vector in there, it ought to be something like: [this.Matrix_base (op.fn this.value x), op.type == Operator_type.COMPOUND_REWRAP], ie. don't strip the .value off x and rely on op.fn to do that, but this breaks in various ways remove all of _Object and redo it, thinking about what we want operators to look like and what we want types to look like. Have a base class for operators that does most of the standard stuff get rid of the operator types rewrap / arithmetic / relational etc. etc. - try point re = x { (x, y) = re; } it's the 're' param, it stops x being bound to the get-real-part-of-complex builtin expands to point re = x { $$x = re; x = re $$x, is_complex $$x = error "bad match"; } add secrets point point.re = x point.re { $$1 $$1.re = point.re; x x.re = x.re ($$x point.re), is_complex ($$x point.re) = error "bad match"; } x compiles to if_then_else ( ( )) ( ) abstracting point.re after var abstract ((S ((Sr (if_then_else )) ((Sl ((Sr (&& )) ((Sr ) ))) true))) ((Sl ((Sr SHARE0[(: )]) ((Sr ) ))) ((REF0 ( )) [ ]))) reduce and get reduce_spine: ( ( (I (1,2)))) reduce_spine: ( ( (I (1,2)))) sym-param found, argh: point.re maybe fix this when we revise the parser would be a good time to add multiple definitions as well - redo name resolution in parser ... scrap the patch thing, instead have a separate 'resolve' step that runs after parsing a top-level don't create ZOMBIE symbols, instead make REF nodes in the tree this binds local references, but leaves external refs dangling we do a final link at load time when we copy into the heap do we need zombies at all now? make a fork for this - we have ws->window_width, can we use the one on Model now instead? - new inplace stuff needs a test suite - 'don't show this dialog again' on delete row dialog, also in prefs box_yesno() could take a string which is the name of a pref to check for ask-or-not - how about something that does: im' = operation_list [a, b, c, d] im it does a fold: im' = d (c (b (a im))) but the intermediate images are reused, so you can do in-place stuff with it we could get rid of lineset! - mac binary has a broken im_text() argh - do we allow [r, g, b] = Image_file "babe.jpg" since ? is band index and list index, it seems to make sense we have image ++ image image ++ [] == image for bandjoin, so that lines up too, I guess hmm reverse image to swap the bands over? heh need to be able to override hd and tl - Ackermann http://shootout.alioth.debian.org/great/benchmark.php?test=ackermann&lang=all&sort=cpu A x y = y + 1, x == 0 = A (x - 1) 1, y == 0 = A (x - 1) (A x (y - 1)); correct test result: A 3 4 == 125 A 3 10 is benchmark test ... we fail with a "C stack overflow" error A 3 9 == 4093 works OK we could make this a lot quicker with some arithmetic streamlining could we do tail-recursion elimination? strictness analysis would help too - Fib http://shootout.alioth.debian.org/great/benchmark.php?test=fibo&lang=all&sort=cpu F x = 1, x == 0 = 1, x == 1 = F (x - 2) + F (x - 1); correct output F 32 == 3524578 cima is ~370s for this (!!) work machine is about 50s dell vostro laptop is 45s without optimiser - turn on DEBUG in heap.c, run the fibonacci benchmark we heap_copy F every time it recurses! because when we heap_copy we don't link recursive references try fixing this ... but remember the problems we had with shared classes when we did link-to-value rather than link-to-copy we also have a huge amount of stuff in the heap, could we trim this down? how does it all get pulled in? is it preferences? in nip1, F is about 4x faster WONTFIX for 7.20 ================ - look at: http://www.eggheadcafe.com/software/aspnet/35467981/programmatic-console-appl.aspx possibly better than nip2-cli.exe - turning a column from Many Stats into a vector for doing arithmetic is very tricky argh add a matrix->vector converter? or maybe a one column or 1 row matrix should also be a vector - try: nip2 Workspaces.uniformshapes2.A1.start_line=21 uniformshapes2.ws --set does not work for non-toplevels - try: A1 = [1] A2 = [x :: x <- A1] change A1, A2 does not update, argh we get: link_expr_new: expr A2.$$lcomp0 references link->child = A1 so perhaps we are updating the local of A2, but not A2? A2 is certainly being marked dirty ... on change of A1 we get: row_dirty_set_single: A1 clear_error = true symbol_dirty_set: A1 (0x1a59480) symbol_dirty_set: A2 (0x1a595a0) symbol_recalculate_check: untitled.A1 row_dirty_clear: A1 row_recomp_all: done row A1 - 0.000143873s row_dirty_set_single: A1 clear_error = false row_dirty_clear: A1 symbol_dirty_clear: A1 (0x1a59480) success: [2] symbol_recalculate_check: untitled.A2 symbol_dirty_clear: A2 (0x1a595a0) success: [1] so maybe A2.something is being updated, but the row is not we now mark a row dirty if a sub-expr is dirty. but in row_renerate(), we don't build subexprs should we mark the subexpr dirty? (or maybe we do?) or should we always copy all subexprs when we copy an expr or only subexprs with no row? do we calc rows outside-in or inside-out? does this affect copying subrows? when do we copy now, the first time a row is made? - we destroy and rebuild all links during recomp (eg. turn on DEBUG in link.c), why is this? can't we only rebuild on a change of source text? - fix the FIXME in itext_clear_edited() or wherever it is - try: start nip2 dir untitled create A2, A3, etc. A1 does not update when we add/remove a def to workspace, should we mark the ws dirty? - lambdas should allow patterns? eg.: map (\[x, y] x + y) [[1, 2], [3, 4]] == [3, 7] - OS X bundler: http://sourceforge.net/apps/trac/gtk-osx/wiki/Bundle test? - imageinfo_make_paintable() no longer copies to a file, since this used to cause problems with dangling pointers because of the im_close()s we had to do however, this means we now do all painting in memory :-( do we need to add API to change a memory image (eg. a "p") into a file? - test Joe's layout thing, compare to the thing we do in study2 to make the diagnostic image - im_blend(), im_ifthenelse(), im_add() etc. now do bandalike/formatalike where do we use our bandalike/formatalike stuff? remove our stuff, though make sure we have equivalents in vips now - outline text example - needs a custom convol menu item which can loop over a group of matricies with a single image actually, we need to nail this down, otherwise when we pass in a list of sample texts the loops don't nest - right-click menu on row button should have items for "Jump to referrer / WC1 / JC1 ..." and "Jump to referred / ..." - why didn't im_copy_file() work? mysterious - line colours are wrong, argh, very mysterious, see plot_new_gplot() - gtk3.0 tests: build with #define G_DISABLE_DEPRECATED #define G_DISABLE_SINGLE_INCLUDES #define GDK_DISABLE_DEPRECATED #define GTK_DISABLE_DEPRECATED #define GDK_DISABLE_SINGLE_INCLUDES #define GTK_DISABLE_SINGLE_INCLUDES paste into config.h, somehow need to remove: GtkType gtk_type_new gtk_signal_connect GTK_SIGNAL_FUNC gtk_signal_handler_block_by_data ` gtk_signal_connect_object GTK_CHECK_CAST GTK_CHECK_TYPE GtkSignalFunc - add Set menu to Math with mkset/union/intersection/difference ? could do bit operations on images? - lcomps like: argh = [x :: x <- poop] the 'x' gets copied inside the lcomp, leaving a zombie 'x' attached to argh, which Program / View / Errors then reports fix: don't resolve names as we parse and junk the ugly patch list thing instead, have a separate resolve stage that runs after we've moved scraps of graph to their final home - if we want full VipsObject introspection we will need a lot more vips_object_arguments (name2gtype "VipsInterpolateYafrsmooth") -> ["sharpness"] need some equivalent for GParamSpec / VipsArgument VipsArgument name type .... = class {} return a list of these from vips_object_arguments()? - heap_map_dict() should be reduce_map_dict(), since it does reduction, argh redo heap_map_dict() in terms of reduce_map_dict() actually, remove all the reduce_ stuff, it's daft to have a distinction something to do when we break Snip out into libsnip - Plot window should have image header menu item? not trivial, image header needs a conversion to watch we'd need to make a conversion in plotmodel - filesel guess-file-type-from-suffix needs fixing copy the vips model of having a user_name which is just "Workspace" or somesuch, and making "Workspace file (*.ws)" string at runtime use this to identity file types in util.c as well: get_image_info() needs it - for rows made by typing stuff, always show the formula as well as the value by default anyway? we'd need to always show the up/down arrows, not just for classes - drag from an image thumbnail to the ws background and you get a new column with "A2" or whgatever in does not work for plot thumbnails! how annoying - right-click on image background to get a context menu with save/replace/header? same as row thumbnail context? - look at using goffice instead of gtkplot for graphs http://ftp.gnome.org/pub/gnome/sources/goffice/ also in synaptic there's also a new cairo-based gtkplot in SVN, apparently - try last [1..] then CTRL-W ... we can quit the app, but it's still evaling and the prompt never comes back - Math / Cluster is a bit useless, will it work for complex numbers? vectors? colours? groups? what would we have to do to get it to work for these other types? - toolkit load is where compiled code would go in need to make load / parse / compile self-contained ... the only output is a list of symbols, each with code and sub-symbols; there are no toolkits or whatever made after load / parse / compile, we need to walk the symbol list building tools and all that stuff we do: load toolkit: get filesize, date last modified, md5sum look in ~/.nip2-7.x.x/cache for a file named with that md5sum if present, open and check first two fields: filesize and date if match, load compiled code if no match, load / parse / compile toolkit, then save compiled code to ~/.nip2-7.x.x./cache walk symbol list building tools and all that stuff how much time will this really save? can we easily get an estimate? steps to follow: 1. make load / parse / compile self-contained, with separate pass to build tools etc. this is a useful cleanup whatever else we do 2. now we know exactly what the output of load / parse / compile is, we should be able to write to a file make our own binary format, don't use XML ... we want speed 3. try loading and benchmarking 4. if the benchmarks look promising, harden and polish - numbering of group of group save seems to skip one at end of line? - Math / Cluster is a bit useless, will it work for complex numbers? vectors? colours? groups? what would we have to do to get it to work for these other types? - segv in test_toolkits on laptop (inside fftw3) ???!? valgrinds cleanly on work machine - try last [1..] then CTRL-W ... we can quit the app, but it's still evaling and the prompt never comes back - configure no longers sets GMSGFMT, is this OK? test on OS X - for rows made by typing stuff, always show the formula as well as the value by default anyway? we'd need to always show the up/down arrows, not just for classes - drag from an image thumbnail to the ws background and you get a new column with "A2" or whgatever in does not work for plot thumbnails! how annoying - right-click on image background to get a context menu with save/replace/header? same as row thumbnail context? - look at using goffice instead of gtkplot for graphs http://ftp.gnome.org/pub/gnome/sources/goffice/ also in synaptic WONTFIX for 7.14 ================ - quit while a thumbnail is painting: IMAGEs are leaked? seems esp. bad with greyc, perhaps just because it's so slow - do we enforce no-args-to-LHS-pattern anywhere? try [a,b] a b = 12; - use destroy_if_destroyed() in more places? grep destroy_ *.h - after pressing "Process" in the edit window, we always select last_sym, which is often not what we want make it jump less after a process ... eg. try editing something in the middle of Image/Transform, very annoying - use compile->last_sym to spot chains of defs multiple definitions of the same function are allowed, provided they all multiple definitions of the same function are allowed, provided they all have the same number of arguments, and provided only one of them has no argument pattern matching example: fred [a, b, 1] = a * b; fred (Image x) = rot90 x; fred z = error "what"; any of these can have locals, those locals just apply to that one definition this compiles to: fred $$a4 = $$fred1, is_list $$a4 && len $$a4 == 2 && $$a4?2 == 1 = $$fred2, is_instance_of "Image" $$a4 = $$fred3 { $$fred1 = a * b { a = $$a4?0; b = $$a4?1; } $$fred2 = rot90 x { x = $$a4; } $$fred3 = error "what"; { z = $$a4; } } so each pattern-matching definition generates a condition and an action constants in patterns become part of the condition test the action goes into a private function, the conditions are joined together in the function wrapper the no-pattern case (if present) becomes the action for the "otherwise" clause in the wrapper if not present, we generate (error "pattern match failed") or somesuch for the default case we will need to regenerate the wrapper function every time a definition of fred is added or removed ... can we do this easily? when are two definitions considered equal? should we warn about this? "fred x" could occur in two files, for example process: * we see a "fred arg-list" incoming * does the arg list contain any patterns? yes: * do we already have a fred in this scope? yes: * the existing fred must be the wrapper, this must be a new possible RHS * check that the number of args matches no: * generate a fred to act as the wrapper * add args called $$arg1 etc. to the main fred * add this new fred as a $$fredn local to the current fred * expand the patterns as local references to the main fred's arguments * parse the rest of the def in that context * keep the pattern list around, we'll need it to generate the ifs for the wrapper later no: * do we have a fred in this scope? yes: * check the previous fred was a pattern matcher * check the number of args matches * check there isn't already a default case * add as above no: * add as a regular non-pattern definition issues: * where do we store the pattern lists? we can't expand them at parse-time, since we need them to make the wrapper (which we can't make until we've seen all the candidate RHS) * when one of the RHS is changed, we need to regenerate the wrapper, how do we do this? (could nuke the generated code in the compile when we see a new RHS, then rebuild the wrapper in compile on the next heap_copy?) * our current condition generator won't work ... we need to test consts as well, and it'll be rather inefficient as we'll repeatedly test the trunk as we loop over the leaves --- instead, walk the pattern recursively top-down testing each node process: * see "fred" (as opposed to simple_pattern) * is there already a fred in scope? yes: * was current_compile->last_sym also a "fred"? yes: * another definition yes: * this must be an alternative definition - we put the 2nd fred in as a local of the first, but then the 2nd can see all the stuff the first has as locals z = 42; fred 1 = 12 { z = 99; } fred 2 = z; "fred 2" will return 99 :( we need to make "fred 2 = z" into another fred at the same level, eg $$alternate42$fred 2 = z; Nope, then how do we link the freds together for remove etc.? Better: see a new sym (fred), create it parse args with simple names becoming params, patterns becoming $$arg42 plus a local $$patt42 holding the pattern source expand the pattern to an access def as well, so our children can bind to it at the = sign, test for any pattern args present .. if there are none, carry on as before otherwise, make a new local called $$alternate42 or whatever and parse the RHS into that at the end of parse, need to resolve outwards twice, since we nest in twice now if we see another fred, check that the number of args matches and then parse in as $$alternate99 what abut fred 2 a = 12; fred 1 b = 32; the first fred will make a top-level with fred $$arg12 a = { $$alernate42 = 12; $$patt12 = 2; } then when we parse the 2nd fred the name of the 2nd param is wrong :( Even betterer: use GLR to split off the four cases for us ident = pattern = ident ident_list = ident pattern_list = change pattern syntax so that ident is not part of simple_pattern need to change lcomp as well so we need to check why PARSE_PARAMS gets used: can we do without the params part? yes, it's used so we can edit functions, but we no longer do this these days all we need is expr I think, but we'd need a small action wrapper around it to wipe out any existing tree and locals Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class Transform b reference.width reference.height { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [a,b,c] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image a; final_error = c; } } fails with Bad superclass. Superclass constructor "Image_transform_item.Image_rubber_item.Transform" should have no secret arguments. but this: Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _t { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [a,b,c] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image a; _t = Transform b reference.width reference.height; final_error = c; } } (ie. make the superclass constructor into a member) works fine - try using bison's location system http://www.gnu.org/software/bison/manual/html_mono/bison.html#Locations - add something to Symbol: Symbol *access_for; links generated access def to the $$thing4 which holds the RHS handy for the program window, also maybe for lcomp code gen? also for row edits - don't offer to clear temps if there's been a crash need to be able to test for process-still-running by PID try http://msdn2.microsoft.com/en-us/library/ms886766.aspx gboolean process_running( int pid ) { HANDLE handle; if( (handle = OpenProcess( 0, FALSE, pid )) ) { CloseHandle( handle ); return( TRUE ); } return( FALSE ); } - autoarrange after every column resize or move animate movement, so columns slither out of the way and in to place when you drop duplicate column placement would be odd: maybe place duplicate below? also new column? what about dropping an image on to the ws background? - there's some left-recursion in the parser, eg. comma_list, is this easily fixable? - add (*) (operator sections) ... need a binup / uop production? can't do this without losing precedence stuff, it'll need a separate production for sections - magic definition maker could make a workspace-local def, rather cool - test classmodel_dict_new() ... part of classmodel member automation need to implement member edit for OPTION groups classmodel_done_member() ... read widget set -> model classmodel_buildedit_member( ... model -> build widget set part of the [["key",value]] arg type - can we get VIPS errors reported in Error too? we'd need to add logging to vips I think - toolkits / find doesn't find builtins on their name ... eg. search for "im_add" too hard to fix with the way searching is done now - turn on update regions during drag, fix the x pos, try dragging, horrible flickering as we update twice, once after the drag motion and once after the recomp if you comment out the explicit vobject_refresh() in regionview_model_update() the flickering goes, but region dragging is then very unresponsive fix this when we get fast recomp back again - have a test_types.ws ... test arithmetic on all combinations of _types? - panner would be cool - tooltips on Expression rows always show unedited formula could special-case formula for things with expression RHS? - what about iimage, iregion, iarrow ... can we member automate these? why are they different? - can't see error indications in noedit mode should set a red background for display area as well as for rowview button? - need to be able to override cons to be able to make a List class :-( see reduce.c:1710 this will change the strictness of cons ... how much breakage will this cause? very unclear try this as a quick hack need to do this before we can finish List need List to make gamma easy - unselected column headers are too like the bg colour on windows? - python uses z.real and z.imag to extract real/image, should we add this too? we don't really have complex as a true class, so it would be rather odd need to add "[a].head" etc as well - python blocks complex->real with casts ... insists you use .imag/.real or abs() - if nip sees a IM_RW_IMAGE argument, it could automatically do this: 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 ); } so it would turn a single IM_RW_IMAGE arg into a paired input and output arg could make im_lineset() into a regular inplace func and rely on nip to wrap and unwrap junk flood_blob_copy nip could do this lazilly ... if we see the user doing im_line (im_line ...) ... then we could make one memory image and call im_line twice on it destructively ... cool! we'd need to check refcounts to make sure the intermediate wasn't being used anywhere else hmm! might actually be very hard, we don't have true refcounts for things in the heap need to do it on read instead: - for image i - use as an IM_RW_IMAGE arg ... copy to a memory area and pass in memory handle - return memory area IMAGE j, and set a flag saying "can operate on destructively" - if we use j as an IM_RW_IMAGE arg, skip the copy and just pass memory area in destructively ... we now have two ImageInfo sharing a single IMAGE - !!!! - does ImageInfo allow IMAGE sharing? not sure it does - maybe this needs to be a vips8 feature when we'll have refcounts on IMAGE - tooltip on column says which other columns items in this column refer to, and which columns refer to items in this column - how about a nip start folder common to all versions so nip2-7.11.14 tries .nip2-7.11.14/start .nip2-7.11/start .nip2-7/start .nip2/start or maybe .nip2/7.11.14/start .nip2/7.11/start .nip2/7/start .nip2/start bit less cluttered also, we could have .nip2/tmp and not have multiple nip2 tmp areas workspace recover after crash could break though ... maybe keep ws saves in .nip2/7.11.4/tmp? - think again about class arg checks is there some way we can avoid the _check overhead? or at least check less often - plotpresent/imagepresent could have a common base class with the focus stuff in? also kb nav, zoom, drag-scroll a bit difficult, because we want two different policies on window resize: plot should change the object to match the window - photographic negative should also be in image/levels ? no, it does ->sRGB, (255-) etc., so it's better as a filter - gtk+ 2.12 has a treeview widget with rectangular select and grid lines use instead of gtksheet? - stop image flickering on clock recomp? want background pattern to be a property of the image display widget, not the image? so we fade in tiles when that section of the image has never been displayed before (eg. on scroll or zoom) we don't fade when that section has been painted and we are just changing the image (eg. on recalc) if fadesteps == 1, only paint the sections of the tile for which mask == 255 this way we will never paint the bg pattern need some hack for scroll/zoom ; test for mask == 255 would be slow :( ================================================ FILE: autogen.sh ================================================ #!/bin/sh # set -x # remove /everything/ ready to remake rm -f Makefile Makefile.in aclocal.m4 config.* configure depcomp rm -rf autom4te.cache rm -f install-sh intltool-* ltmain.sh missing mkinstalldirs rm -f src/*.o src/nip2 src/Makefile src/Makefile.in # glib-gettextize asks us to copy these files to m4 if they aren't there # I don't have $ACDIR/isc-posix.m4, how mysterious ACDIR=`aclocal --print-ac-dir` # OS X with brew sets ACDIR to # /usr/local/Cellar/automake/x.y.z/share/aclocal, the staging area, which is # totally wrong argh if [ ! -d $ACDIR ]; then ACDIR=/usr/local/share/aclocal fi mkdir -p m4 cp $ACDIR/codeset.m4 m4 cp $ACDIR/gettext.m4 m4 cp $ACDIR/glibc21.m4 m4 cp $ACDIR/iconv.m4 m4 cp $ACDIR/lcmessage.m4 m4 cp $ACDIR/progtest.m4 m4 # some systems need libtoolize, some glibtoolize ... how annoying echo -n "testing for glibtoolize ... " if glibtoolize --version >/dev/null 2>&1; then LIBTOOLIZE=glibtoolize echo using glibtoolize else LIBTOOLIZE=libtoolize echo using libtoolize fi aclocal # this produces a lot of benign but misleading output ... hide it and hope for # the best glib-gettextize --force --copy > /dev/null test -r aclocal.m4 && chmod u+w aclocal.m4 autoconf autoheader $LIBTOOLIZE --copy --force --automake automake --add-missing --copy ./configure $* ================================================ FILE: configure.ac ================================================ # Process this file with autoconf to produce a configure script. AC_INIT([nip2], [8.9.2], [vipsip@jiscmail.ac.uk]) # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) AM_INIT_AUTOMAKE([-Wno-portability foreign]) AC_CONFIG_HEADERS(config.h) AC_CONFIG_MACRO_DIR([m4]) dnl dnl We do the version number components as m4 macros dnl so that we can base configure --help output off dnl of them. dnl m4_define([nip_major_version], [8]) m4_define([nip_minor_version], [9]) m4_define([nip_micro_version], [2]) m4_define([nip_version], [nip_major_version.nip_minor_version.nip_micro_version]) MAJOR_VERSION=nip_major_version() MINOR_VERSION=nip_minor_version() MICRO_VERSION=nip_micro_version() AC_DEFINE_UNQUOTED(MAJOR_VERSION, $MAJOR_VERSION, [Major version number]) AC_DEFINE_UNQUOTED(MINOR_VERSION, $MINOR_VERSION, [Minor version number]) AC_DEFINE_UNQUOTED(MICRO_VERSION, $MICRO_VERSION, [Micro version number]) AC_CANONICAL_HOST AC_MSG_CHECKING([for native Win32]) case "$host" in *-*-mingw*) nip_os_win32=yes ;; *) nip_os_win32=no ;; esac AC_MSG_RESULT([$nip_os_win32]) if test x"$nip_os_win32" = "xyes"; then AC_DEFINE(OS_WIN32,1,[native win32]) # makes gcc use win native alignment IP_CFLAGS="-mms-bitfields $IP_CFLAGS" fi # src/Makeile.am uses this to add an icon to the .exe AM_CONDITIONAL(OS_WIN32, test x"$nip_os_win32" = "xyes") AC_MSG_CHECKING([for Mac OS X]) case "$host" in *-*-darwin*) nip_os_darwin=yes ;; *) nip_os_darwin=no ;; esac AC_MSG_RESULT([$nip_os_darwin]) if test x"$nip_os_darwin" = "xyes"; then AC_DEFINE(OS_DARWIN,1,[native Mac OS X]) fi AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug=@<:@no/minimum/yes@:>@], [turn on debugging @<:@default=debug_default()@:>@]),, enable_debug=no) if test "x$enable_debug" = "xyes"; then NIP_DEBUG_FLAGS="-DDEBUG_FATAL -DDEBUG_LEAK" else NIP_DEBUG_FLAGS="-DG_DISABLE_CAST_CHECKS" if test "x$enable_debug" = "xno"; then NIP_DEBUG_FLAGS="$GLIB_DEBUG_FLAGS -DG_DISABLE_ASSERT -DG_DISABLE_CHECKS" fi fi IP_CFLAGS="$NIP_DEBUG_FLAGS $IP_CFLAGS" # we want largefile support, if possible AC_SYS_LARGEFILE # Checks for programs. AC_PROG_AWK AC_PROG_CC AM_PROG_CC_C_O AC_PROG_LEX # we must have flex or lex if test x"$LEX" = x:; then AC_MSG_ERROR([lex/flex not found: $PACKAGE requires one of these]) fi IP_LIBS="$IP_LIBS $LEXLIB" AC_PROG_INSTALL AC_PROG_LN_S AC_CHECK_TOOL(WINDRES, windres) AC_CHECK_TOOL(DLLWRAP, dllwrap) AC_CHECK_TOOL(DLLTOOL, dlltool) AC_CHECK_TOOL(OBJDUMP, objdump) AC_CHECK_TOOL(RANLIB, ranlib) AC_CHECK_TOOL(STRIP, strip) AC_CHECK_TOOL(BISON, bison) # we have to have bison :-( maybe we could ship the generated .c/.h files? not # clear on their portability if test x"$BISON" = x; then AC_MSG_ERROR([bison not found: $PACKAGE uses bison-only features]) fi AC_CHECK_TOOL(AR, ar) AC_CHECK_TOOL(AS, as) AC_CHECK_TOOL(LD, ld) AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL # dmalloc option AM_WITH_DMALLOC # i18n GETTEXT_PACKAGE=nip2 AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [The prefix for our gettext translation domains.]) # ALL_LINGUAS="en_GB malkovich" ALL_LINGUAS="en_GB" AM_GLIB_GNU_GETTEXT # check for flex ... nip needs to adjust itself a bit if test "${LEX}" = "flex"; then AC_DEFINE(HAVE_FLEX,1,[using flex, rather than lex]) fi # flex >= 2.5.36 uses a nonstandard type for yyleng AC_MSG_CHECKING([whether yyleng is yy_size_t]) cat > conftest.l <= 2.4.9 libxml-2.0 vips >= 7.30) IP_CFLAGS="$REQUIRED_PACKAGES_CFLAGS $IP_CFLAGS" IP_LIBS="$REQUIRED_PACKAGES_LIBS $IP_LIBS" # gdk_window_set_opacity() was added in gtk 2.12 PKG_CHECK_EXISTS(gtk+-2.0 >= 2.12, [nip_set_opacity=yes], [nip_set_opacity=no] ) if test x"$nip_set_opacity" = x"yes"; then AC_DEFINE(HAVE_SET_OPACITY,1,[define if you have gdk_window_set_opacity()]) fi # GtkInfoBar was added in gtk 2.18 PKG_CHECK_EXISTS(gtk+-2.0 >= 2.18, [nip_use_infobar=yes], [nip_use_infobar=no] ) if test x"$nip_use_infobar" = x"yes"; then AC_DEFINE(USE_INFOBAR,1,[define if you have GtkInfoBar]) fi # notebook action widgets came in 2.20 PKG_CHECK_EXISTS(gtk+-2.0 >= 2.20, [nip_use_notebook_action=yes], [nip_use_notebook_action=no] ) if test x"$nip_use_notebook_action" = x"yes"; then AC_DEFINE(USE_NOTEBOOK_ACTION,1,[define if you have gtk_notebook_set_action_widget()]) fi # notebook group names widgets came in 2.24 PKG_CHECK_EXISTS(gtk+-2.0 >= 2.24, [nip_use_notebook_group_name=yes], [nip_use_notebook_group_name=no] ) if test x"$nip_use_notebook_group_name" = x"yes"; then AC_DEFINE(USE_NOTEBOOK_GROUP_NAME,1,[define if you have gtk_notebook_set_group_name()]) fi # GRegex was added in glib-2.14 # we need it for regex searching in the program window PKG_CHECK_EXISTS(glib-2.0 >= 2.14, [nip_use_gregex=yes], [nip_use_gregex=no] ) if test x"$nip_use_gregex" = x"yes"; then AC_DEFINE(HAVE_GREGEX,1,[define if you have GRegex]) fi # Check for the function strccpy in libgen AC_CHECK_HEADER(libgen.h, AC_CHECK_LIB(gen, strccpy, AC_DEFINE(HAVE_STRCCPY,1,[have strccpy() in -lgen]) IP_LIBS="$IP_LIBS -lgen" ), ) # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(limits.h pwd.h fnmatch.h sys/statvfs.h sys/vfs.h sys/mount.h sys/resource.h sys/wait.h malloc.h sys/time.h sys/param.h unistd.h) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_STRUCT_TM # Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_FNMATCH AC_FUNC_VPRINTF AC_CHECK_FUNCS(geteuid getcwd getpwnam getrlimit getpwent getwd putenv regcomp strcspn strspn strstr) # need fftw so we load and unload wisdom on startup/shutdown AC_ARG_WITH([fftw3], AS_HELP_STRING([--without-fftw3], [build without fftw3 (default: test)])) if test "x$with_fftw3" != "xno"; then PKG_CHECK_MODULES(FFTW3, fftw3, [AC_DEFINE(HAVE_FFTW3,1,[define if you have fftw3 installed.]) with_fftw3=yes ], [AC_MSG_WARN([fftw3 not found; disabling fftw support]) with_fftw3=no ]) IP_CFLAGS="$FFTW3_INCLUDES $FFTW3_CFLAGS $IP_CFLAGS" IP_LIBS="$FFTW3_LIBS $IP_LIBS" fi # goffice needs libgsf to save plots to files AC_ARG_WITH([libgsf], AS_HELP_STRING([--without-libgsf], [build without libgsf (default: test)])) if test "x$with_libgsf" != "xno"; then PKG_CHECK_MODULES(LIBGSF, libgsf-1, [AC_DEFINE(HAVE_LIBGSF,1,[define if you have libgsf installed.]) with_libgsf=yes ], [AC_MSG_WARN([libgsf not found; disabling save plot to file]) with_libgsf=no ]) IP_CFLAGS="$LIBGSF_CFLAGS $LIBGSF_INCLUDES $IP_CFLAGS" IP_LIBS="$LIBGSF_LIBS $IP_LIBS" fi # optional ... use libgoffice to draw plots # pretty basic functionality, really, but we need to be able to build without # it for testing AC_ARG_WITH([libgoffice], AS_HELP_STRING([--without-libgoffice], [build without libgoffice (default: test)])) if test "x$with_libgoffice" != "xno"; then PKG_CHECK_MODULES(LIBGOFFICE, libgoffice-0.8, [AC_DEFINE(HAVE_LIBGOFFICE,1,[define if you have libgoffice installed.]) with_libgoffice=yes ], [AC_MSG_WARN([libgoffice not found; disabling plot display]) with_libgoffice=no ]) IP_CFLAGS="$LIBGOFFICE_CFLAGS $LIBGOFFICE_INCLUDES $IP_CFLAGS" IP_LIBS="$LIBGOFFICE_LIBS $IP_LIBS" fi # optional ... use libgvc to draw graphs of workspace dependencies AC_ARG_WITH([libgvc], AS_HELP_STRING([--without-libgvc], [build without libgvc (default: test)])) # gvc 2.30 is broken in a number of ways and we can't use it, see for example # http://lists.research.att.com/pipermail/graphviz-devel/2012/001544.html if test "x$with_libgvc" != "xno"; then PKG_CHECK_MODULES(LIBGVC, libgvc > 2.30, [AC_DEFINE(HAVE_LIBGVC,1,[define if you have libgvc installed.]) with_libgvc=yes ], [AC_MSG_WARN([libgvc not found; disabling workspace dep graph display]) with_libgvc=no ]) IP_CFLAGS="$LIBGVC_CFLAGS $LIBGVC_INCLUDES $IP_CFLAGS" IP_LIBS="$LIBGVC_LIBS $IP_LIBS" fi # optional ... we add some gsl funcs as builtins if available AC_ARG_WITH([gsl], AS_HELP_STRING([--without-gsl], [build without gsl (default: test)])) if test "x$with_gsl" != "xno"; then PKG_CHECK_MODULES(GSL, gsl, [AC_DEFINE(HAVE_GSL,1,[define if you have gsl installed.]) with_gsl=yes ], [AC_MSG_WARN([gsl not found; disabling extra numerical functions]) with_gsl=no ]) IP_CFLAGS="$GSL_CFLAGS $GSL_INCLUDES $IP_CFLAGS" IP_LIBS="$GSL_LIBS $IP_LIBS" fi # optional ... use this to open the help browser, if available AC_PATH_PROG(XDG_OPEN, xdg-open, no) if test "x$XDG_OPEN" != "xno"; then AC_DEFINE(HAVE_XDG_OPEN,1,[define if you have xdg-open]) AC_DEFINE_UNQUOTED(XDG_OPEN, "$XDG_OPEN", [path of xdg-open binary]) fi # optional ... use these to update desktop after install AC_PATH_PROG(UPDATE_MIME_DATABASE, update-mime-database, no) AC_PATH_PROG(UPDATE_DESKTOP_DATABASE, update-desktop-database, no) nip_desktop_update=no if test "x$UPDATE_MIME_DATABASE" != "xno"; then if test "x$UPDATE_DESKTOP_DATABASE" != "xno"; then nip_desktop_update=yes fi fi # stop the DBs being updated: useful for packagers AC_ARG_ENABLE(update-desktop, AC_HELP_STRING([--disable-update-desktop], [disable update of desktop database]), [nip_desktop_update=$enableval],) if test x"$nip_desktop_update" = "xyes"; then AM_CONDITIONAL(UPDATE_DESKTOP, true) else AM_CONDITIONAL(UPDATE_DESKTOP, false) fi # we always need -lm IP_LIBS="$IP_LIBS -lm" AC_SUBST(IP_CFLAGS) AC_SUBST(IP_LIBS) # needed by test/test_all.sh # :( what's a better way to do this, argh TOP_SRCDIR=$ac_pwd AC_SUBST(TOP_SRCDIR) AC_OUTPUT([ nip2.desktop Makefile man/Makefile man/man1/Makefile share/Makefile share/nip2/Makefile share/nip2/data/Makefile share/nip2/rc/Makefile share/nip2/start/Makefile share/nip2/compat/Makefile share/nip2/compat/7.8/Makefile share/nip2/compat/7.9/Makefile share/nip2/compat/7.10/Makefile share/nip2/compat/7.12/Makefile share/nip2/compat/7.14/Makefile share/nip2/compat/7.16/Makefile share/nip2/compat/7.24/Makefile share/nip2/compat/7.26/Makefile share/nip2/compat/7.28/Makefile share/nip2/compat/7.38/Makefile share/nip2/compat/7.40/Makefile share/nip2/compat/8.2/Makefile share/nip2/compat/8.3/Makefile share/nip2/compat/8.4/Makefile share/nip2/compat/8.5/Makefile share/nip2/compat/8.6/Makefile src/BITMAPS/Makefile src/Makefile test/Makefile test/test_all.sh po/Makefile.in nip2.spec ]) # generated script needs to be executable chmod +x test/test_all.sh AC_MSG_RESULT([ * general build options native win32: $nip_os_win32 native os x: $nip_os_darwin update desktop after install: $nip_desktop_update debug: $enable_debug * optional packages and modules use fftw3 for FFT: $with_fftw3 use gsl for numeric functions: $with_gsl use libgoffice to show plots: $with_libgoffice use libgsf to save plots to files: $with_libgsf use libgvc to show ws dep graphs: $with_libgvc (requires gvc > 2.30) use gtkinfobar to show messages: $nip_use_infobar (requires gtk+-2.0 >= 2.18) use notebook action widget: $nip_use_notebook_action (requires gtk+-2.0 >= 2.20) use notebook group name: $nip_use_notebook_group_name (requires gtk+-2.0 >= 2.24) allow regex searches: $nip_use_gregex (requires glib-2.0 >= 2.14) display help files with xdg: $XDG_OPEN ]) ================================================ FILE: doc/README ================================================ nip documentation Type "make install" to rebuild html/ and ps/ directories with formatted docuimentation. You'll need latex and tex4ht. The Makefile.am in nip2-x.x copies the contents of the html/ and ps/ to $prefix/share/doc/nip2 during the main nip2 install process. Once you have the docs installed, rebuild nip2's help index with: cd nip2-x.x/src make helpindex.h to index the HTML docs and link the HELP buttons in nip to the correct place in the formatted pages. ================================================ FILE: doc/doc-programmer/hierarchy.txt ================================================ class hierarchy =============== GObject | +-iObject | +-Heap +-Imagemodel +-Toolviewitemgroup +-iContainer | +-Compile +-Expr +-Imageinfo +-Watch +-Model | +-Conversion +-Toolkitgroup +-Workspacegroup +-Filemodel | | | +-Tool | +-Toolkit | +-Column | +-Symbol | | | +-Workspace | +-Heapmodel | +-Rhs +-Subcolumn +-iText +-Row +-Classmodel | +-Slider +-Patch +-Filename +-Fontname +-Expression +-Number +-Matrix +-String +-Option +-Toggle +-iRegiongroup +-iArrow +-iImage | +-iRegion GtkVBox View Textview Workspacegroupview Workspaceview Toolkitgroupview Toolkitview Toolview Graphicview Toggleview Sliderview Patchview Filenameview Fontnameview Optionview iArrowview Matrixview Regionview iImageview iRegionview Editview Stringview Numberview Expressionview Rhsview Rowview Subcolumnview Spin Columnview iRegiongroupview Imagepresent GtkFrame Conversionview Statusview GtkDrawingArea Imagedisplay GtkWindow iWindow Imageview Program Trace iDialog Namecaption Find Imageheader Browse Filesel GtkEventBox Formula ================================================ FILE: doc/doc-programmer/imageview ================================================ imagemodel is the base model class ... imageview_new makes the imagemodel, then all the component widgets (imagepresent, statusview, paintboxview, conversionview, imageview) watch this for updates the conversion from the real image to the display image is handled by conversion, a sub-model of imagemodel imageview holds the ref to imagemodel ... destroy this and everything goes ================================================ FILE: doc/doc-programmer/makeindex.pl ================================================ #!/usr/bin/perl # my labels = `grep ' device ICC_import device -> PCS ICC_transform device -> device ICC_ac2rc ------- D65XYZ_to change white point for measure/print D50XYZ D50XYZ_to D65XYZ D50XYZ_to Lab D50Lab_to XYZ ------- Sharpen_for_print Morph_for_print Relational Equal Not_equal More Less More_equal Less_equal Resize Resize_image change size by a scale factor Resize_xy_image separate xy scale factors Resize_canvas ------- Shrink_to Quicklook shrink to smallest axis == 64 pixels Icon shrink to smallest axis == 400 pixels Rotate Rotate_fixed r90 r180 r270 r45 Rotate_free rotate with a slider ------- Flip up_down left_right Transpose ------- Straighten_arrow rotate to get an arrow straight Statistics Mean Deviation Stats ------- Max Min Maxmin Maximum_position Minimum_position ------- Count_set Count_clear ------- Measure_colour_chart Statistical_difference more of a filter really :-( Count_lines Trig Sin Cos Tan ------- Asin Acos Atan ------- Rad Deg ------- Angle_range is angle within arc ... clock arithmetic ================================================ FILE: doc/doc-programmer/regionview ================================================ how regionviews are built and maintained iregion iregionview end of parent_add, make iregiongroup child iregiongroup iregiongroupview monitor object updates, on iregiongroupview_refresh() create and set and unset model->display destroy regionview ... watches iregion model regionview_new( iregion, area, imagepresent ) does not view_link(), since it's not a true view ... adds own signal handlers for "changed" and "destroy" on iregion/iarrow ================================================ FILE: doc/src/Example.def ================================================ /* Gamma im gam: correct */ Gamma A1 B7 = error "arg 1 is not image", not is_image A1; = error "arg 2 is not number", not is_number B7; = Convert_to_unsigned_char B5 { B4 = A1 ^ (1/B7); B5 = B4 * (255 / 255 ^ (1/B7)); }; ================================================ FILE: doc/src/html.cfg ================================================ % configuration file for output of nipguide as html \Preamble{html} \begin{document} % stop the mono font shrinkage we do for paper output \renewenvironment{ctd}{\begin{quote}\tt}{\end{quote}} % make a label % in html, write an extra label which we can link to nip's help system \renewcommand{\mylabel}[1]{ \label{#1} \HCode{} } % supress " on page xx" if we're making HTML \renewcommand{\onpage}[1]{} \EndPreamble ================================================ FILE: doc/src/infrared.tex ================================================ \chapter{Assembling infrared mosaics} \mylabel{sec:ir} VIPS has a package of functions designed to help join many small images together to make a single large image. They were originally designed to assemble infrared reflectograms but are general enough to be useful for other sorts of image as well, such as X-rays. This chapter first introduces the mechanics of infrared imaging then explains how to use \nip{} to assemble the images you grab. Finally, it suggests some printing techniques. \section{Infrared imaging} Most museums use tube cameras (usually called Vidicons) for infrared imaging. Although they are relatively cheap they are not very stable and they suffer from (sometimes quite severe) geometric distortions. More modern solid-state cameras are still expensive but are becoming more widely used because of their greater stability. This guide assumes you are using a tube camera but almost all of it applies to solid-state cameras as well. Whatever your camera there are three main sources of error which have to be addressed in order to be able to make successful mosaics: \begin{enumerate} \item Tube cameras suffer very badly from distortions in the image, usually either `pin-cushioning' or `barrelling'. These distortions result in alignment errors when sub-images are joined together. \item The sensitivity of the tube varies across its surface, causing some parts of each sub-image to be brighter than others. This is made worse by unavoidable variations in illumination. When a lot of such images are joined together the result is a `brick wall' effect. \item The sensitivity of the tube also varies between sub-images, partly as the overall lightness in the field of view changes, and also because the electronics in the camera change as the camera heats up. This leads to a patchy, unbalanced mosaic. \end{enumerate} The first two problems will be different in each Vidicon and will change each time a tube is replaced. All three problems need to be addressed to create successful infrared reflectogram mosaics. \subsection{Setting up your system} \subsubsection{Mechanical set-up} It is vital that the optical axis of the Vidicon is at right-angles to the picture plane and that, whether it is the Vidicon or the painting that moves during image capture, it remains perpendicular. Obviously, with a seriously warped panel, this may not be possible. The lighting should be carefully adjusted so that the area of interest is lit as evenly as possible. If you can, arrange for the lights to remain stationary with respect to the camera. An easy way to test camera alignment is to image a piece of graph paper, move the camera (either left-right or up-down) by 90\% of the field of view, and see how features in the overlap area move. First rotate the centre around the optical axis to get the centre line of the images lined up. Next check the corners and adjust camera pitch and yaw. \subsubsection{Video set-up} \mylabel{sec:vidpref} On Linux, you can capture video directly into \nip{} provided that your capture card is compatible with v4l. You may need to adjust the \nip{} video settings. These settings can be found under the headings \ct{Video for linux} and \ct{General video capture} in the \ct{Preferences} window, which can be accessed by selecting \ctr{Edit}\ct{Preferences} from the main \nip{} window. The default settings, see \fref{fg:vidpref}, are for the Hauppauge PCI capture card and should be changed as required. \begin{fig2} \figw{3in}{scr21a.png} \caption{Recommended video preference settings} \mylabel{fg:vidpref} \end{fig2} Once you have set up your card, select \ctr{Tasks}\ctr{Capture}\ct{Capture Video Frame} to create a new video object, which will appear as a still image in your current selected column. The captured image can be updated by opening the image in a viewing window and then pressing Ctrl-C (a shortcut for \ctr{File}\ct{Recalculate Image}). You can create more than one video object: this can help you to get the overlaps right if you have two open at once (one showing the previous grab) when you are moving the camera around. \subsubsection{Setting the crop and aspect} While it is possible to correct geometric distortions after the image is captured, it is difficult to do the necessary modelling accurately and reliably. Instead, we suggest the grabbed images should simply be cropped, since the most severe distortion affects the perimeter of each video image. To determine the area to crop, set up the Vidicon as it would be set to image a painting and capture an image of a rectangular grid --- a piece of graph paper works well as a target. Before capturing the grid, check the current video crop settings in the \ct{Preferences} window and ensure that the crop is set at maximum: left 0, top 0, width 768, height 576 (these are the dimensions for a PAL signal, they may be different on your system). Also set the \ct{Aspect ratio} line to 1. Create a new video object and look for the largest rectangle with little distortion (no more than a few pixels). If you create a region on the video image (by holding down the Ctrl key and dragging down and right with the left mouse button, see \pref{sec:region}) you can compare the straight edges of the region against the distorted lines of the grid. You can then expand and contract the region until you decide on the optimum area. The settings you need for the crop box can be taken from the values contained within the region object, which can be viewed by left-clicking several times on the down arrow to the left of the region object name. Finally, you can set an aspect ratio: \nip{} will automatically stretch video frames vertically by this factor. You can measure the aspect ratio of your capture card by taking a picture of something you know to be square and dividing the width in pixels by the height in pixels. Move back to the \ct{Preferences} window and enter the crop values in the \ct{General video capture} section. Next time you create a new video object, you should find that it is cropped to the appropriate area. The settings you enter in the \ctr{Preferences} window will be saved and automatically loaded again next time you start \nip{}. See appendix~\ref{sec:config}. Choosing the usable area of the image is a matter of compromise --- the smaller the area, the more images are required to build a mosaic of a particular painting. If the area chosen is too large then the amount of distortion can cause serious errors in the final mosaic. It's possible to use the VIPS rubber sheet plug in to detect and correct geometric distortion in your images automatically. This lets you use the full area of the sensor. It is a bit fiddly, but see the rubber sheet documentation if you are determined. \subsection{Capturing the data} \subsubsection{Setting the gain and offset} Once everything is correctly set up, position the painting in front of the camera and experiment with the gain and offset settings on the Vidicon to achieve the optimum infrared image on the computer screen. Note that the appearance of the image on the computer will normally be different to its appearance on the video monitor connected directly to the camera. Repeat this process at different points across the whole area to be captured. Although it is not always possible, the aim is to find a setting that does not need altering much during the capture of the data. This seems to lead to more successful mosaics. \subsubsection{Grey card correction} To counteract the problem of uneven sensitivity across the target area of the tube, it is necessary to capture an image of a piece of grey card. This grey card image is then used to correct all subsequent images. The card should be a flat, even grey, of around 50\% reflectance. The Vidicon and lighting positions with respect to the painting should not be changed between the imaging of the grey card and the capturing of the mosaic. A grey card can be captured at any time, but it is good practice to start with one --- you may forget later. If your mosaic will take several hours to capture, you may wish to grab extra grey cards, since the sensitivity of the tube can change as it warms up. Be sure to note which data images correspond to which grey cards! In association with the first grey card, it is a good idea to grab an image of your grid to record the scale at which the data images are being made. \subsubsection{Image capture} \mylabel{sec:imcap} Open a video window, \ctr{Tasks}\ctr{Capture}\ct{Capture Video Frame}, and when it shows the desired area, save the file. The choice of file name is a question of personal preference. We find it helpful to use a format that indicates where in the mosaic the data comes from --- for example, \ct{dat3.5.v} for the fifth image in row three. It is helpful to be able to see the previous image to ensure that there is sufficient overlap. To achieve this, a second video window can be opened and placed alongside and the images grabbed into alternate windows. \subsubsection{Capture tips} \begin{itemize} \item Make a new directory for each painting, and keep all of the image files for that painting (including a grey card and a grid) in that directory. \item VIPS can join up images in any layout, but you will get much less confused when you assemble your images if you stick to a regular grid. This can be difficult --- a good compromise is to keep one axis fixed and grab in rows (or columns). \item You can help to reduce mosaicing errors later if you keep your rows (or columns) as short as possible. So if the painting is in landscape format, grab in columns (or turn the painting on its side and grab in rows); if the painting is portrait, grab in rows (or turn the painting on its side and grab in columns). \item The semi-automatic mosaic functions (see \pref{sec:mosaicing}) need a minimum overlap between the sub-images they join of around 20 pixels. For safety, you should aim for a larger overlap than this: we recommend an overlap of 60 pixels (around 20mm, usually). \item If the overlap area is featureless, it is worth identifying a good tie-point and ensuring it is visible in both images, even if this means increasing the overlap for one of the joins. \end{itemize} \subsection{Correcting illumination} \mylabel{sec:grey} Before the mosaic can be assembled, the data images need to be corrected for non-uniformity of illumination using the grey card image. This function can be performed within \nip{} or directly using a predefined VIPS command-line tool. \subsection{Correcting with the command-line tool} \mylabel{sec:linuxgrey} First, close \nip{} and open a command line window, (xterm, mingw, etc). Move to the directory containing your image files with \ct{cd}. For example, if you have made a directory called \ct{raphael} inside your home directory, type: \begin{verbatim} prompt% cd raphael \end{verbatim} The program you need to use is called \ct{light\_correct}. You need to give it the name of the grey-card image and the names of all of the image files you want it to correct with that gray card. Suppose you have saved your gray card image as \ct{grey.v}, and your painting image files are called \ct{dat1.1.v} and \ct{dat1.2.v}. You would then enter: \begin{verbatim} prompt% light_correct grey.v dat1.1.v dat1.2.v \end{verbatim} The program will run and print messages explaining its progress. It creates a new set of corrected image files, with the same names as before, but prefixed with \ct{ic\_}. In this example, it would create two new images files called \ct{ic\_dat1.1.v} and \ct{ic\_dat1.2.v}. If there are a lot of image files to correct this could mean a lot of typing. Fortunately, you can use wildcard characters to abbreviate lists of file names. The example above can be abbreviated to: \begin{verbatim} prompt% light_correct grey.v dat*.v \end{verbatim} \noindent The \ct{dat*.v} means `any filename which starts \ct{dat} and ends with \ct{.v}'. You can use this technique to correct different parts of your mosaic with different grey cards. If you have a file called \ct{grey1.v} for the first row in your mosaic, and a file called \ct{grey2.v} for the second, you could do the correction in two parts: \begin{verbatim} prompt% light_correct grey1.v dat1.*.v \\ prompt% light_correct grey2.v dat2.*.v \end{verbatim} \subsection{Correcting within \nip{}} \mylabel{sec:wingrey} The function within \nip{} used to preform this correction is \ctr{Tasks}\ctr{Capture}\ct{Flatfield}. A set of images can be corrected at the same time by joining them together in a \ct{Group}. A group can be produced by selecting all of the required images and then using the \ctr{Edit}\ct{Group} command. Load all of your images into \nip{}, and group all the image except the grey image. Select your grey image and then your new group and then run the \ctr{Tasks}\ctr{Capture}\ct{Flatfield} function. This will produce you a group of corrected images. Right-click on this new group and select \ct{Save As} from the menu. In the save window type in a name, for example \ct{fred\_01.v} and then hit the save button. All of the images in your group will then be saved as \ct{fred\_01.v}, \ct{fred\_02.v}, \ct{fred\_03.v} \ldots{} \ct{fred\_n.v}. If you want to keep row numbers in your file names, (see \pref{sec:imcap}), you will need to correct your images one row at a time, saving each row as \ct{fred01\_01.v}, \ct{fred02\_01.v}, etc. \section{Assembling the mosaic} \mylabel{sec:mosaicing} The tutorial has a section on mosaic assembly with \nip{}: see \pref{sec:irtut}. Mosaic assembly is normally painless. There are a few factors you should bear in mind when you are deciding how to assemble an image (particularly a large image): \begin{itemize} \item You can open up a mosaic join and change a few options, such as the blend width. If you want to change the defaults for a whole workspace, change the \ct{Mosaic defaults} options in your \ct{Preferences}. \item If two images just won't join correctly, try using \ctr{Tasks}\ctr{Mosaic}\ctr{One Point}\ct{Manual Left to Right} instead. These functions operate in the same way as the usual mosaic functions, but do not do a search. This is useful when the overlap is too small for the search to work correctly, or when the overlap area is very smooth and contains too few features for the search to find the exact overlap for you. \item \nip{} does not do sub-pixel interpolation. As a result, each join will on average cause a positioning error of about 0.5 pixels, even if your input images contain no geometric distortion. If there are distortions in your input images (there usually are distortions in infrared images), then errors can be as much as 1--2 pixels per join. These errors accumulate as the number of images you join becomes larger. If you join a strip of 10 images together with \ctr{Tasks}\ctr{Mosaic}\ctr{One Point}\ct{Left to Right}, on average you can expect a total error of about 5 pixels. If you join two strips like this together top-bottom, you can therefore expect a mismatch of about 2.5 pixels at each end of the join. You can minimise the effect of these errors if you assemble your images differently. Suppose you have a 10 by 10 mosaic to build. Instead of making and joining 10 strips of 10 images each, make four 5 by 5 sub-mosaics (one for each quadrant) and then join these four quadrants together. The errors will now be more evenly spread over the image and therefore will be less visible. \item \mylabel{sec:pieces} Some operating systems limit the number of files a program can have open at once: this in turn limits the size of the mosaics you can assemble in one go. If you are having problems putting together very large mosaics, try building your image in sections (\ct{top}, \ct{middle} and \ct{bottom}, perhaps), and later load up and join these larger pieces. \end{itemize} \section{Balancing the mosaic} \mylabel{sec:balance} Like assembly, mosaic balancing is normally automatic and painless. You may sometimes have problems if the image is very large, or needs dramatic corrections: \begin{itemize} \item Each VIPS image file has an associated history, recording the operations on that image since it was loaded from a file. You can view an image's history by clicking on \ctr{View}\ct{Image header} in an image view window. The automatic balancer uses the history to work out how you built your mosaic. The balancer knows about left-right and top-bottom joins, but nothing else! If the history has other stuff recorded in there, you'll see unhelpful error messages like \ct{unable to open tmp/xxx.v}, or \ct{more than one root}. If you need to perform corrections to any of your sub-images, do them, save the image, load it again, and then build the mosaic. This will make sure the history of the image you are trying to balance only contains mosaic operations. \item On some systems the balancer can run out of memory or out of file descriptors on very large mosaics. If your mosaic is made up of more than a few hundred images, and you are having balancing problems you may have hit one of these limits. The solution (as with mosaic assembly) is to assemble and balance your mosaic in smaller pieces. \item If your grey-card correction is not accurate, you will find that the balancer will magnify any problems you have. Suppose your lighting and camera set-up always produces images which are brighter on the right than the left, and suppose, due to some problem with your grey-card correction, this effect is not completely removed. You will find that when you balance a mosaic, the small differences between left and right edges of your sub-images will have been smoothed out, but they will have caused a large difference in brightness between the extreme left edge of your final image and the extreme right. \nip{} includes several functions which can help to fix this problem, the most commonly used being: \ctr{Tasks}\ctr{Mosaic}\ctr{Tilt Brightness}\ct{Left to Right} and \ctr{Tasks}\ctr{Mosaic}\ctr{Tilt Brightness}\ct{Top to Bottom}. \end{itemize} \section{Other \nip{} features useful for reflectograms} You can use \nip{}'s general image processing facilities to play around with reflectogram mosaics. You can make false-colour images of your reflectograms, blend them with visible images or X-rays, search them for edges, and so on see the \ct{registering} and \ct{overlays\_and\_blending} examples for ideas. There are also some first order mosaic functions: \ctr{Tasks}\ctr{Mosaic}\ctr{Two Points}\ct{Left to Right} and \ctr{Tasks}\ctr{Mosaic}\ctr{Two Points}\ct{Top to Bottom}. These functions automatically rotate and scale the right-hand image in a join. They are useful for assembling X-ray mosaics, and for fixing very difficult joins in reflectogram images. These functions work the same way as the \ct{One Point} functions except that you will need to define two tie-points on each image. You can mosaic images of any numeric type: 16-bit integer images are handy for mosaicing X-ray images, for example. \section{Printing} Once you have assembled a good reflectogram, you will want to print it, or to use it in other computer programs. The best way to do this is to save the final image in TIFF or JPEG format, and then load it into the new application --- see~\pref{sec:loadsave}. There are a couple of points to bear in mind: first, like any image, reflectograms look best on paper if you sharpen them up a little first. Click on \ctr{Filter}\ctr{Convolution}\ct{Custom Convolution}, right click on the matrix button, select \ct{Replace from file}. Double Click on the second or lower \ct{data} directory listed in the left hand column to enter \nip{}'s main data directory. Change the \ctr{Image type select} option to \ct{All FIles (*)} and then select and load \ct{rachel.con}. This will usually produce an approprioatly sharpened reflectogram. Secondly, you will need to try several prints with different contrasts and brightnesses to get a good match between the paper and the screen, try \ctr{Image}\ctr{Levels}\ct{Linear}. You may even want to fiddle with the gamma, try \ctr{Image}\ctr{Levels}\ct{Power}. Finally, you may not need a full resolution image. For almost all printers there's no point going over about 300 dpi (dots per inch), or about 3000 by 2000 pixels for an A4 page. To reduce the size of an image, use one of the functions listed under \ctr{Resize}\ctr{Transform}\ct{Resize}. ================================================ FILE: doc/src/intro.tex ================================================ \chapter{Getting started} \noindent \nip{} is a user interface for the VIPS image processing library. It is designed to be fast, even when working with very large images, and to be easy to extend. This guide is split into quite a few chapters: \begin{itemize} \item If you want to use \nip{} to assemble infrared mosaics, you should read \cref{sec:ir}. The middle section in the tutorial (see \pref{sec:irtut}) does IR mosaics very quickly. \item If you want to use \nip{} for general image processing, work through \cref{sec:tutorial}. \item If you have specific questions about some part of \nip{}'s user-interface, look at \cref{sec:reference}. \item If you're really hardcore, take a look at \cref{sec:program}, which covers programming. \item If you want to know more about VIPS, the image processing package underlying \nip{}, try the \emph{VIPS Manual}. \end{itemize} If \nip{} has installed correctly you should see something like \fref{fg:introwin} when it starts up. \begin{figure} \figw{3in}{snap1.jpg} \caption{\nip{} as it starts up} \label{fg:introwin} \end{figure} ================================================ FILE: doc/src/menus.tex ================================================ \chapter{Image processing menus} \mylabel{sec:menus} \noindent This chapter is runs quickly through the \ct{Toolkits} menu. See \cref{sec:program} if you want to understand how the menus are written (or want to add more of your own). Use the Toolkit Browser to find stuff. Some things are common to almost all menu items: \begin{description} \item[Tooltips] If you rest your mouse pointer over an item, you'll see a quick description of what the item does. \item[Grouping] You can select several objects, click \ctr{Edit}\ct{Group}, and then when you click the item, it will operate on all the objects in the group. \item[Any type] Almost all items will work on any object. You can add an image and a number, for example, find the colour difference between a number and an image, or transform a matrix from LAB to XYZ. \end{description} \section{Colour} \mylabel{sec:menu-colour} This menu groups operations on colorimetric images and patches of colour. A colour patch is three float numbers plus a tag saying how those number should be interpreted as colour (for example, as a colour in CIE LAB colourspace). You can drag and drop between colour patches, and into and from the inkwell in an image paint window. Double-left-click on a colour patch to open a colour select dialog. \nip{} has 9 main types of colorimetric image, see \tref{tb:colour}. All these types are D65 (that is, daylight) absolute colorimetric. When it displays an image, \nip{} uses the \ct{Type} field in the image header as a hint on how to transform the numbers in the image into RGB for the display. The current \ct{Type} is displayed at the end of the caption line below an image thumbnail. The \ct{Mono}, \ct{GREY16} and \ct{RGB16} types are not really calibrated themselves: they are usually whatever you get by loading an image from a file. You'll usually need an extra step, such as applying an embedded ICC profile, before you get accurate colour. \begin{tab2} \begin{center} \begin{tabular}{||l|l|l||} \hline Name & Format & Notes \\ \hline \ct{Mono} & One band 8 bit & Not calibrated \\ \ct{sRGB} & Three band 8 bit & Screen device space for the sRGB standard \\ \ct{GREY16} & One band 16 bit & Not calibrated \\ \ct{RGB16} & Three band 16 bit & Not calibrated \\ \ct{Lab} & Three band float & The 1976 version of the CIE perceptual colourspace \\ \ct{LabQ} & Four band 8 bit & Like \ct{Lab}, but represented as 10:11:11 bits \\ \ct{LabS} & Three band 16 bit & Like \ct{Lab}, but represented as 15:16:16 bits \\ \ct{LCh} & Three band float & \ct{Lab}, but with polar coordinates \\ \ct{XYZ} & Three band float & The base CIE colourspace \\ \ct{Yxy} & Three band float & Sometimes useful for colour meters \\ \ct{UCS} & Three band float & Highly uniform space from the CMC(l:c) standard \\ \hline \end{tabular} \end{center} \caption{\nip{} colourspaces} \mylabel{tb:colour} \end{tab2} \begin{description} \item[\ct{New}] Make a patch of colour, or pick a colour from a slice through CIELAB colourspace. \item[\ct{Convert To Colour}] Convert anything into a Colour object. \item[\ct{Colourspace}] Change the colourspace. The stored numbers change, but the visual appearance should stay the same. \item[\ct{Tag As}] Change the colourspace tag (the \ct{Type} field in the image header). The stored numbers stay the same, but the visual appearance should change. \item[\ct{Colour Temperature}] Change the colour temperature. \ct{Move Whitepoint} just adjusts the ratios of X and Z using the CIE standard illuminants. \ct{D65 to D50} and \ct{D50 to D65} transform using either a 3x3 matrix which is numerically minimal in XYZ space with respect to the colours on a Macbeth Color Checker, or via Bradford cone space. The Bradford transform omits the power term. The final two items go from XYZ to LAB and back, but with D50 normalisation rather than the default D65. \item[\ct{ICC}] Transform images (not patches of colour) device space to profile connection space (LAB float) and back. You need to be careful about colour temperature issues: all printers work with D50, and \nip{} is all D65. Use the D65 to D50 interchange items in the \ct{Colour Temperature} menu to swap back and forth. All printers also work with relative colorimetry, and \nip{} is generally absolute. Use \ct{Absolute to Relative} to scale an absolute colorimetric image by a media white point. \item[\ct{Radiance}] \nip{} can read and write images written by the Radiance family of programs (usually with the suffix \ct{.hdr}), commonly used in HDR photrography. Images in this format used a packed floating point layout for their pixels. Items in this menu pack and unpack pixels for you. \item[\ct{Difference}] Calculate various colour difference metrics. You can mix patches of colour and colour images. \item[\ct{Adjust}] Change colour in a colorimetric way. \ct{Recombination} multiplies each pixel in an image through a matrix. \ct{Cast} displaces the neutral axis in LAB space. \ct{HSB} lets you adjust an image in LCh colourspace. \item[\ct{Similar Colour}] find pixels in an image with a similar colour to a patch of colour. \item[\ct{Measure Colour Chart}] This takes a trimmed image of a colour chart (a rectangular grid of coloured squares), measures the average pixel value in the centre 50\% of each square, and returns a matrix of the measured values. Use \ct{Make Synthetic Colour Chart} to make a colour chart image from a matrix of measurements. \item[\ct{Plot ab Scatter}] draws a 2 dimensional histogram of the distribution of pixel colours in LAB colourspace. \end{description} \section{Filter} \mylabel{sec:menu-filter} This menu groups operations which filter images, or which are filters in the photoshop sense. \begin{description} \item[\ct{Convolution}] This menu has several standard convolution operations (blur, sharpen, edge detect, etc.), plus the option to convolve with a custom kernel. Two menu items are slightly more complicated. \ct{Unsharp Mask} transforms to CIE LAB colour space, then sharpens just the L band with a cored unsharp filter. The \ctr{Tasks}\ct{Print} menu has a version of this filter tuned for typical inkjet printers. \ct{Custom Blur} builds and applies a square or gaussian convolution kernel for you based on a radius setting. \item[\ct{Rank}] A preset median filter, and a custom rank filter that lets you specify window size and rank. The \ct{Image Rank} item does pixel-wise ranking of a set of images. \item[\ct{Morphology}] These menu items implement basic morphological operations. Images are zero for background and non-zero (usually 255) for object. Matricies are shown as 0, 1 and * for background, object and don't-care. The \ct{Threshold} item does a simple level threshold. Use the \ctr{Math}\ct{Relational} menu to construct more complex image binarisations. Use \ctr{Math}\ct{Boolean} to combine morphologies. The first half of the menu lists simple erode and dilate operations, 4- and 8-way connected. The second half contains several useful compound filters. See also \ctr{Histogram}\ct{Find Profile} for something that can search an image for object edges. And \ct{Math}\ctr{Statistics}\ct{Edges} can count the number of edges across and down an image. \item[\ct{Fourier}] A selection of ideal, Gaussian and Butterworth Fourier space filters. You can make other mask shapes yourself using the \ctr{Image}\ct{Make Patterns} menus, then apply them using \ctr{Math}\ct{Fourier}. You can also use the image paintbox to directly paint out peaks in a fourier-space image before transforming back to real space. \item[\ct{Enhance}] A selection of simple image enhancement filters. \ct{Statistical Difference} passes a window over an image and tries to match the region statistics at each point to a target mean and deviation. \item[\ct{Spatial Correlation}] Place a small image at every possible position in a big image and calculate the correlation at each position. \ct{Simple Difference} is the much faster unnormalised version. \item[\ct{GREYCstoration}] VIPS includes a copy of the CImg library and you can use two useful CImg operations from this menu: denoising and enlarging. \item[\ct{Tilt Brightness}] A selection of tools for adjusting the brightness of an image across it's surface. Useful for correcting lighting problems. \item[\ct{Blend}] Blend two objects together using either a third object to control the blend at each point, or a slider to set all points together. You can blend almost anything with anything. One useful version is to use a text image (see \ctr{Image}\ctr{Make Patterns}\ct{Text}) to blend between two colours (see \ctr{Colour}\ct{New}). \ct{Along Line} does a left/right or top/bottom fade between two images. \item[\ct{Overlay}] Make a colour overlay of two monochrome images. Useful with \ctr{Image}\ctr{Transform} for testing image superposition. \item[\ct{Colourize}] Use a colour image to tint a monochrome image. Useful in conjunction with \ctr{Image}\ctr{Transform}. \item[\ct{Browse}] Look at either the bits or the bands of an image. \item[\ct{Photographic Negative} and friends] A small selection of simple, faintly photoshop-style filters. \end{description} \section{Histogram} \mylabel{sec:menu-histogram} This menu groups operations for finding and transforming image histograms. \nip{} represents histograms and lookup tables as images with \ct{Type} set to \ct{Histogram}. Histograms may have pixels in any format and any number of bands. You can only find histograms of unsigned 8- and 16-bit images. \begin{description} \item[\ct{New}] This makes a new ramp histogram. A set of sliders let you adjust the shape. Use \ct{Map Histogram} to apply your ramp to an image. \ct{Build LUT from Scatter} makes a histogram from a matrix of $(x, y)$ values. \ct{Tag Image as Histogram} marks an image as actually being a histogram after all. \ct{Tone Curve} builds a tone curve which you can later apply to an image. \item[\ct{Find}] A one dimensional histogram treats each band as an independent variable. An $n$-dimensional histogram treats each pixel as a vector of $n$ elements, where $n$ is the number of bands in the image. \item[\ct{Map}] Looks up each pixel in the input in the histogram and sends the found value to the output. \item[\ct{Equalise}] Find the global or locally histogram equalised image. \item[\ct{Cumulative}] Use this and friends to calculate a cumulative histogram (integrate), normalise a histogram and match two histograms. \item[\ct{Find Profile}] Searches from the edges of an image for the first non-zero pixel and returns a profile histogram. \item[\ct{Find Projections}] Sum columns and rows in an image. \item[\ct{Plot Slice}] Mark a guide on an image (drag from the image rulers, or click \ctr{File}\ctr{New}\ct{Guide}) and click \ct{Plot Slice} to make a histogram which is a horizontal or vertical slice through an image. Use \ct{Extract Arrow} to extract the area around an arrow or guide. Use \ct{Plot Object} to make a plot of any object. \end{description} \section{Image} \mylabel{sec:menu-image} This menu groups operations which apply only to images. \begin{description} \item[\ct{New}] Makes a new image. \ct{Region on Image} makes a new region, arrow, guide or mark on an image. It's usually easier to open a viewer on an image and Ctrl-drag. \item[\ct{Convert to Image}] Try to make an image out of anything. \item[\ct{Format}] Switch between the various precisions. \item[\ct{Header}] Try to change or examine the image header in various ways. \item[\ct{Cache}] This caches an image in RAM. Use this to save the results of a long computation. \item[\ct{Levels}] Various tools that change the levels in an image. \ct{Tone Curve} is the only complex one: it lets you adjust the image levels with a set of sliders. \item[\ct{Transform}] Various tools that change the geometry of an image. To use \ctr{Rotate}\ct{Straighten}, mark an arrow on an image (Ctrl-drag up and left in an image view window) along a near-horizontal or near-vertical edge. When you click on \ctr{Rotate}\ct{Straighten}, \nip{} will rotate the image by the smallest amount that makes that edge exactly horizontal or vertical. \ct{Linear Match} takes two images and rotates and scales the second so that the images can be superimposed. Drag the tie-=points to mark common features. Use \ctr{Filter}\ct{Overlay} or \ctr{Filter}\ct{Colourize} to actually superimpose them. \ct{Rubber Sheet} is useful for fixing things like lens distortion. You give \ct{Find} two images, a reference and a distorted version of that reference, and it automatically finds a transform which will map the distorted image back on to the reference image. Use \ct{Apply} to apply the discovered transform to another image. \item[\ct{Band}] Extract/insert/delete image bands. Use \ct{To Dimension} to change image bands into a horizontal or vertical dimension. Use \ct{To Bands} to compress the horizontal or vertical dimension into bands (small images only!). \item[\ct{Crop}] Crops an image. It's often easier to drag out a region. This menu item is only really useful for cropping large groups of images. \item[\ct{Insert}] This takes two images and pastes the smaller into the centre of the larger. The two images have to have the same number of bands. If you open an image viewer on the large image, you'll see an area which you can drag around to set the exact insert point. \item[\ct{Select}] Draw elipses and polygons on an image. Useful for selecting defined areas. \item[\ct{Join}] Use to join two images together bandwise, left/right or up/down. \ct{Array} joins a list of lists of images together into a single large image. \item[\ct{Tile}] Repeat an image horizontally and vertically to make a larger image, or chop an image into a set of tiles. \item[\ct{Patterns}] These items all make useful images for you, from checkerboards to gaussian masks. \ct{XY Image} is the most useful: you can use it to build other patterns. \item[\ct{Test Images}] These items make a variety of useful testcharts for evaluating spatial response and colour. \end{description} \section{Math} \mylabel{sec:menu-math} Basic maths operations on any combination of any objects. You can add a slider to a matrix, for example, then divide by an image. Hopefully most of these are obvious. \begin{description} \item[\ctr{Arithmetic}\ct{Absolute Value Vector}] The absolute value item normally calculates mod of each band of an image separately. By contrast, \ct{Absolute Value Vector} treats each pixel as a vector and calculates the modulus of that. \item[\ct{List}] These aren't really maths operations, but they're in here too. \end{description} \section{Matrix} \mylabel{sec:menu-matrix} This menu groups operations which operate on matricies. \nip{} has four ways of displaying a matrix, but they all behave in the same way under the skin. Almost all the items in the \ct{Math} menu will work on matricies. Most of the matrix operations will also work on images. \begin{description} \item[\ct{New}] The first four items make matricies which display and edit in various ways useful for different applications. The final two make matricies which are pre-filled with useful numbers. \item[\ct{Convert to Matrix}] Try to make anything into a matrix. \item[\ct{Extract}] This group of items extracts various submatricies. You can also do this graphically: just drag-select an area in matrix. \item[\ct{Insert}, \ct{Delete}, \ldots] Also work on images, which can be handy. A 45 degree rotate will only work for square matricies with odd-length sides. \item[\ct{Invert}] Simple matrix-only maths operations. \item[\ct{Plot Scatter}] This takes a two-column matrix where the columns are the X and Y positions of points and draws a scatter graph. \end{description} \section{Object} \mylabel{sec:menu-object} This groups a few items which had no obvious home and which change the format of objects. \begin{description} \item[\ct{Duplicate}] Copy an object, stripping off any derived classes. For images, this really takes a copy of the underlying object (using \ct{im\_copy()}). \item[\ct{List to Group}] Changes lists (see \ctr{Math}\ct{List}) into Groups (see \ctr{Edit}\ct{Group}) and back. A list os an ordered collection of objects. A group is a list that \nip{} will automatically iterate over. \item[\ct{Break Up Object}] This tries to take an object apart. So a multi-band image becomes a list of 1-band images. A matrix becomes a list of vectors, and so on. \ct{Assemble Object} is the inverse. \end{description} \section{Tasks} \mylabel{sec:menu-tasks} This menu repeats many items from other menus, but tries to group them by tasks they are useful for, rather than by function. \subsection{Capture} \mylabel{sec:menu-capture} This menu groups operations which are useful in capturing images, or for the initial processing you might want to do to an image captured from another program. \begin{description} \item[\ct{CSV Import}] Import an image from a CSV file, with a few controls. \item[\ct{Interpret Analyze 7 Header}] Read the meta fields for volume layout and calibration from the Analyze header and reformat the image appropriately. \item[\ct{Capture Video Frame}] This menu item will currently only work on Linux machines with a compatible video4linux capture card. See \pref{sec:vidpref} for notes on how it works. \item[\ct{Smooth}] Use this to remove texture from images. It's handy in conjunction with \ct{Flatfield}. \item[\ct{Flatfield}] Use this to correct homogeneity. Select an image of a piece of white (or mid-grey) card, then select the image to correct, then click \ct{Flatfield}. Use \ct{Smooth} to renmove texture from the white card if necessary. You can select a single white and a group of images to correct a large set in one step \item[\ct{White Balance}] Use this to move the white point to make an area of the image you know to be white, white. Mark a region on an image, enclosing a patch you know to be white. Select the region and the image and click on \ct{White Balance}. \item[\ct{Find Colour Calibration}] Use this to colour calibrate an image. Drag a region enclosing an image of a Macbeth Color Checker Chart and click \ct{Find Colour Calibration}. \item[\ct{Apply Colour Calibration}] Use this to apply the transform calculated by the previous item to another image. Select the calibration object, select the RGB image you want calibrated, and click \ct{Apply Colour Calibration}. \end{description} \subsection{Mosaic} \mylabel{sec:menu-mosaic} The items in this menu are discussed in appalling detail in \cref{sec:ir}. \begin{description} \item[\ct{One Point}] Join two images left-right or top-bottom with a simple translation. Mark a point on each image to be joined (open image view window, Ctrl-left-click, drag to position), then click on the mosaic button. The operation performs elaborate tie-point adjustment, so your selection of a common feature does not have to be exact. The \ct{Manual} versions do not perform automatic tie-point correction and are useful when joing very difficult images. \item[\ct{Two Point}] Do a join, but allow the right-hand (or bottom) image to rotate and scale if it will improve the match. You need to pick two points on each image. \item[\ct{Balance}] Break a mosaic apart, examine average pixel value in the overlap regions, adjust brightness to match, and reassemble. This only works for images which have been produced just by mosaic joins! If you've done anything else to the image since loading it, the balance will fail with a mysterious message. \item[\ct{Manual Balance}] Adjust the brightness in a set of masked areas to match. Useful for removing shadows. \item[\ct{Rebuild}] Use this to mosaic up one set of files based on joins you made in another. Breaks a mosaic part to component files, performs a string substitution on the file names, and reassembles. \item[\ct{Clone Area}] Select over- or under-exposed pixels in one image and replace them with the corresponding pixels from another image. Useful for removing lead numbers used to identify X-ray plates. The function operates on two 8-bit mono images. Move and resize the region on the first image to define the area around the white number. Move the region on the second to overlapping area. A section of the area on the second image is cloned and blended into the first image. The amount of the defined area to be cloned in defined by a slider within the output image. \end{description} \subsection{Picture Frame} \mylabel{sec:menu-picture-frame} Items useful for mocking up painting frames. \subsection{Print} \mylabel{sec:menu-print} Items useful while preparing an image for printing. \begin{description} \item[\ct{Sharpen}] Sharpen an image for printing. This is a version of \ctr{Filter}\ctr{Convolution}\ct{Unsharp Mask} tuned for typical inkjet printers. \item[\ct{Adjust Tone Curve}] Adjust the reproduction tone curve in LAB. Most useful for offset work, especially from transparencies. \end{description} ================================================ FILE: doc/src/mydefs.tex ================================================ % My defs % Computer Text, Computer text=>, Computer Text Display \newcommand{\ct}[1]{\textsf{\smaller{}#1}} \newcommand{\ctr}[1]{\ct{#1} / } \newenvironment{ctd}{\begin{quote}\footnotesize\tt}{\end{quote}} \pagecolor{white} % abbreviations \newcommand{\nip}{\ct{nip2}} \newcommand{\bs}{$\backslash$} \newcommand{\rtp}{\^{ }} \newcommand{\cielab}{\emph{CIE~}$L^{*}a^{*}b^{*}$} \newcommand{\ciexyz}{\emph{CIE XYZ}} \newcommand{\cross}{$\times{}$} % make a label ... override this for HTML output and insert an anchor \newcommand{\mylabel}[1]{\label{#1}} % generate " on page xx" if a label is referring to something on another page % override this for HTML output \newcounter{boink} \newcommand{\onpage}[1]{% \addtocounter{boink}{1}% \label{atref\theboink{}}% \ifthenelse{\pageref{atref\theboink{}}=\pageref{#1}}% {}% { on page~\pageref{#1}}} % format a reference to a section .. "$3.11 on page 37" \newcommand{\pref}[1]{\S\ref{#1}\onpage{#1}} \newcommand{\tref}[1]{Table~\ref{#1}\onpage{#1}} \newcommand{\fref}[1]{Figure~\ref{#1}\onpage{#1}} \newcommand{\cref}[1]{Chapter~\ref{#1}\onpage{#1}} \newcommand{\aref}[1]{Appendix~\ref{#1}\onpage{#1}} % Insert a file ... height and name. \newcommand{\fig}[2]{ \begin{center} \includegraphics[height=#1]{figs/#2} \end{center} } % Insert a file ... width and name. \newcommand{\figw}[2]{ \begin{center} \includegraphics[width=#1]{figs/#2} \end{center} } % make a 2-column figure ... define our own so we can easily override in html % output \newenvironment{fig2}{\begin{figure*}}{\end{figure*}} % same for 2-col tables \newenvironment{tab2}{\begin{table*}}{\end{table*}} % causes problems for htlatex :-( % make this a noop for now % \newcommand{\dtxt}[1]{\multicolumn{25}{@{\hspace{0.2em}}l}{#1}} \newcommand{\dtxt}[1]{#1} % Insert a blank page \newcommand{\blankpage}{% \newpage ~~~~ \pagestyle{plain} \newpage % Another one necessary in twocolumn mode ~~~~ \newpage \pagestyle{fancy} } %\addtolength{\headheight}{3pt} % Make text a bit wider, since we are two column. \addtolength{\textwidth}{0.5in} \addtolength{\oddsidemargin}{-0.25in} \addtolength{\evensidemargin}{-0.25in} % twocolumn seems to remove the binding offset ... add it back %\addtolength{\oddsidemargin}{-0.2in} %\addtolength{\evensidemargin}{0.2in} % More space between headers and footers and the body \addtolength{\topmargin}{-0.5em} \addtolength{\headsep}{0.5em} \addtolength{\footskip}{0.5em} % Swap left and right binding offsets \newlength{\fred} \setlength{\fred}{\oddsidemargin} \setlength{\oddsidemargin}{\evensidemargin} \setlength{\evensidemargin}{\fred} ================================================ FILE: doc/src/nipguide.tex ================================================ \documentclass[a4paper,twocolumn,dvips]{book} \usepackage[dvips=false,pdftex=false,vtex=false]{geometry} \usepackage{relsize} \usepackage{ifpdf} \ifpdf \usepackage[pdftex]{graphicx,color} \else \usepackage{graphicx,color} \fi \usepackage{times} \usepackage{fancyhdr} \usepackage{ifthen} \input{mydefs} \fancyhead{} % clear all fields \fancyhead[LE,RO]{\leftmark} % left-even, right-odd \fancyhead[RE,LO]{\nip{} Manual} % right-even, left-odd \fancyfoot[LE,RO]{\thepage} % left-even, right-odd \fancyfoot[RE,LO]{December 2017} \begin{document} \pagenumbering{roman} \begin{titlepage} \thispagestyle{empty} \begin{center} \huge \nip{} Manual\\[0.2em] \large Version 8.6\\ \vspace{0.5in} \large John Cupitt, Rachel Billinge, Joseph Padfield, Clare Richardson, David Saunders\\ \end{center} % hmm ... must be a better way to get the quote at the bottom of the page \vspace{3in} \begin{center} \noindent \emph{``It's quite simple really, and at the same time, rather complicated.''} \\ --- A.\ Haddock, Sea captain (rtd.) \end{center} \vspace{3in} \noindent \small{This document formatted \today} \setcounter{page}{1} \end{titlepage} %\blankpage \tableofcontents \thispagestyle{plain} %\blankpage \listoffigures \thispagestyle{plain} %\blankpage \listoftables \thispagestyle{plain} \blankpage \pagenumbering{arabic} \thispagestyle{plain} \cfoot{} \input{intro} \input{tutorial} \input{infrared} \input{reference} \input{menus} \input{program} \appendix \input{config} \end{document} ================================================ FILE: doc/src/program.tex ================================================ \chapter{Programming} \mylabel{sec:program} \noindent \nip{} includes a tiny lazy functional programming language. You can use it to glue VIPS image processing functions together to perform more complicated tasks. All of the \nip{} toolkit menus are written in this language. These first sections just describe the programming language. See \pref{sec:progwin} for a description of the programming window. You use \nip{}'s programming language to control the user interface: the link between what happens inside a \nip{} function and what you see on the screen in covered in \pref{sec:bowser}. \section{Load and save} When \nip{} starts up it loads all of the definition files (files with a \ct{.def} extension) it can find in the directories listed in your start path. You can change the start path in Preferences. By default, the start path lists just two areas: a personal start directory that \nip{} makes in your home area, and the main system \nip{} start directory containing all the standard toolkits. If there are two files with the same name on the start path, then \nip{} will only load the first one. This means that if you modify one of \nip{}'s built-in menus and save it to your personal start directory, in future you'll just see your personalised version. You can load or reload a toolkit at any time with the \ctr{File}\ct{Open Toolkit} menu item in the program window. If you open a toolkit with the same name as an existing toolkit, \nip{} will remove the old toolkit before it loads the new one. \section{Using an external editor} If you're going to be doing any more than a little programming in \nip{} you probably won't want to use the built-in editor. I suggest you start your favorite editor in one window on the screen and then in the \nip{} program window click \ctr{File}\ct{Open Toolkit} and check the Pin-up box in the file selector. Now every time you want to try out your definition, save the file from your external editor and click OK in \nip{}'s file selector. \nip{}'s editor automatically adds some semicolon characters to separate definitions in a file. If you're using an external editor, you'll need to put these in yourself. Also check the syntax for adding separators and column items to menus. \section{Syntax} The most basic sort of definition looks like this: \begin{verbatim} // very simple! fred = 12 \end{verbatim} \noindent This defines a function called \ct{fred} whose value is the number 12. The \ct{//} marks a comment: everything to the end of the line is skipped. Case is distinguished, so \ct{Fred} and \ct{fred} are two different functions. You can use letters, numbers, underscores and single quotes in function names. You can have patterns on the left of the equals sign. For example: \begin{verbatim} [fred, petra] = [12, 13] \end{verbatim} \noindent defines \ct{fred} to have the value 12 and \ct{petra} to have the value 13. See \pref{sec:pattern} for details. Functions may take parameters: \begin{verbatim} /* A function with parameters. */ jim a b = a + b + 12 \end{verbatim} \noindent This defines a function called \ct{jim} which takes two parameters and whose value is the sum of the two parameters, plus 12. The \ct{/*} and \ct{*/} enclose a multi-line comment. Functions may have several right-hand-sides, each right-hand-side qualified by a guard expression. Guards are tested from top to bottom and the first guard which has the value \ct{true} causes the function to have the value of that right-hand-side. If no guard evaluates to \ct{true}, then the last right-hand-side is used. \begin{verbatim} jenny a b = 42, a + b >= 100 = 43, a + b >= 50 = 44 \end{verbatim} \noindent This defines a function called \ct{jenny} which takes two parameters and whose value is 42 if the sum of the parameters is 100 or greater; 43 if the sum is greater than or equal to 50 but less than 100; and 44 if the sum is less than 50. Any function may be followed by any number of local functions, enclosed in curly braces. So \ct{jenny} could be written as: \begin{verbatim} jenny a b = 42, sum >= 100 = 43, sum >= 50 = 44 { sum = a + b; } \end{verbatim} \noindent Note that you need a semi-colon after each local function. A local function may refer to anything in an enclosing scope, including itself. You can write \ct{if-then-else} expressions: \begin{verbatim} david a = if a < 12 then "my cat" else "likes lasagne" \end{verbatim} \noindent This is exactly equivalent to: \begin{verbatim} david a = "my cat", a < 12 = "likes lasagne" \end{verbatim} \noindent \ct{if-then-else} expressions are sometimes easier to read than guards. Functions application is with spaces (juxtaposition). For example: \begin{verbatim} harry = jim 2 3 \end{verbatim} \noindent defines \ct{harry} to have the value 17. All functions are curried, that is, they can accept their arguments in stages. For example: \begin{verbatim} sandro = jim 1 \end{verbatim} \noindent defines \ct{sandro}, a function which takes one parameter and will add 13 to it. This trick becomes very useful with list processing, see \pref{sec:lists}. \nip{} has some built-in functions, see \tref{tb:builtin}. They mostly take a single argument. All other functions are defined in the various standard toolkits and can be edited in the program window. \begin{tab2} \begin{center} \begin{tabular}{||l|l||} \hline Function & Description \\ \hline \ct{dir} \textit{any} & List names in scope \\ \ct{has\_member} \textit{[char]} \textit{any} & Does class have member \\ \hline \ct{name2gtype} \textit{[char]} & Search for a GType by name \\ \ct{gtype2name} \textit{real} & Return the name of a GType \\ \hline \ct{error} \textit{[char]} & Stop with error message \\ \ct{print} \textit{any} & Convert to string \\ \ct{expand} \textit{[char]} & Expand environment variables in string \\ \ct{search} \textit{[char]} & Search for a file \\ \ct{\_} \textit{[char]} & Translate string \\ \hline \ct{is\_image} \textit{any} & Test for image \\ \ct{is\_bool} \textit{any} & Test for boolean \\ \ct{is\_real} \textit{any} & Test for real \\ \ct{is\_class} \textit{any} & Test for class \\ \ct{is\_char} \textit{any} & Test for char \\ \ct{is\_list} \textit{any} & Test for list \\ \ct{is\_complex} \textit{any} & Test for complex \\ \ct{is\_instanceof} \textit{[char]} \textit{any} & Test for instance of class \\ \hline \ct{re} \textit{image}/\textit{complex}/\textit{class} & Extract real part of complex \\ \ct{im} \textit{image}/\textit{complex}/\textit{class} & Extract imaginary part of complex \\ \ct{hd} \textit{list} & Extract head of list \\ \ct{tl} \textit{list} & Extract tail of list \\ \ct{sin} \textit{image}/\textit{number}/\textit{class} & Sine \\ \ct{cos} \textit{image}/\textit{number}/\textit{class} & Cosine \\ \ct{tan} \textit{image}/\textit{number}/\textit{class} & Tangent \\ \ct{asin} \textit{image}/\textit{number}/\textit{class} & Arc sine \\ \ct{acos} \textit{image}/\textit{number}/\textit{class} & Arc cosine \\ \ct{atan} \textit{image}/\textit{number}/\textit{class} & Arc tangent \\ \ct{log} \textit{image}/\textit{number}/\textit{class} & Natural log \\ \ct{log10} \textit{image}/\textit{number}/\textit{class} & Base 10 log \\ \ct{exp} \textit{image}/\textit{number}/\textit{class} & e to the power \\ \ct{exp10} \textit{image}/\textit{number}/\textit{class} & 10 to the power \\ \ct{ceil} \textit{image}/\textit{number}/\textit{class} & Round up \\ \ct{floor} \textit{image}/\textit{number}/\textit{class} & Round down \\ \ct{gammq} \textit{real} \textit{real} & Normalised incomplete Gamma function \\ \hline \ct{vips\_image} \textit{[char]}& Load image from file \\ \ct{read} \textit{[char]} & Load file as a string \\ \hline \end{tabular} \end{center} \caption{\nip{} built in functions} \mylabel{tb:builtin} \end{tab2} \section{Naming conventions} You can name things in any way you like, but we've used the following conventions. \begin{itemize} \item Classes start with a capital letter, words are separated with underscores, subsequent words are not capitalised (eg. \ct{Image\_file}) \item Private names are prefixed with underscores (and are hidden by most of the user interface) \item Functions from the VIPS library are prefixed with \ct{im\_} \item Global utility functions (eg. \ct{map}), public members (eg. \ct{Colour.colour\_space}) are all lower case, words are separated with underscores, subsequent words are not capitalised \item Constants are capitalised (eg. \ct{Operator\_type.COMPOUND\_REWRAP}) \end{itemize} \section{Evaluation} \nip{} calculates the value of an expression by using the definitions you entered to successively reduce the expression until it becomes one of the base types. Sometimes there is a choice as to which part of the expression will be reduced next --- \nip{} will always choose to reduce the leftmost, outermost part of the expression first. For example, consider this definition: \begin{verbatim} factorial n = n * factorial (n - 1), n > 1 = 1 \end{verbatim} And here's how \nip{} will evaluate the expression \ct{factorial 3}: \begin{verbatim} factorial 3 --> 3 > 1 --> true 3 * factorial (3 - 1) --> (3 - 1) > 1 --> 2 > 1 --> true 3 * (2 * factorial (2 - 1)) --> (2 - 1) > 1 --> 1 > 1 --> false 3 * (2 * 1) --> 3 * 2 --> 6 \end{verbatim} \noindent Note how \nip{} delays evaluating parameters to functions until they are needed, but still shares the result. \ct{3 - 1} is only evaluated once, for example, even though the result is used three times. \nip{} has a trace window: click on \ctr{Debug}\ct{Trace} in the program window and check the \ctr{View}\ct{Operators} menu item. The advantage of this style of computation over conventional imperative programming languages is that you can reason about your program mathematically\footnote{Since programs are referentially transparent (that is, the value of an expression depends only upon its syntactic context, not upon computation history), you can easily do equational reasoning, proof by induction, and so on. Expressions are like theorems, definitions are like axioms, computation is like proof.}. This isn't the best way to write a factorial function. A function with lots of recursive calls can be hard to understand --- it's much better to use one of the higher order functions from the standard environment to encapsulate the type of recursion you want to use. The clearest definition for factorial is probably: \begin{verbatim} factorial n = product [1..n] \end{verbatim} \noindent See \pref{sec:listsyntax} for an explanation of the list syntax. \section{Operators} \mylabel{sec:operators} \nip{}'s expression syntax is almost exactly the same as C, with a few small changes. \tref{tb:precedence} lists all of \nip{}'s operators in order of increasing precedence. If you've used C, the differences are: \begin{itemize} \item C's \verb+?:+ operator becomes \ct{if-then-else}, see above \item Like almost every functional language, \nip{} uses square brackets for list constants (see \pref{sec:listsyntax}), so to index a list, \nip{} uses \ct{?} \item \nip{} adds \ct{@} for function composition, see \pref{sec:func} \item The \ct{:} operator is infix list cons, see \pref{sec:lists} \item The \ct{++} operator becomes an infix concatenation operator, \ct{--} becomes list difference. Again, see \pref{sec:listsyntax} \end{itemize} The only slightly tricky point is that function application binds very tightly (only list index and class project bind more tightly). So the expression: \begin{verbatim} jim = fred 2 + 3 \end{verbatim} \noindent binds as: \begin{verbatim} jim = (fred 2) + 3 \end{verbatim} \noindent This is almost always the behaviour you want. There are two special equality tests: \ct{===} and \ct{!==}. These test for pointer equality, that is, they return \ct{true} if their arguments refer to the same object. These are occasionally useful for writing interactive functions. \begin{tab2} \begin{center} \begin{tabular}{||l|l|l||} \hline Operator & Associativity & Description \\ \hline \ct{if then else} & Right & If-then-else construct \\ \ct{=>} & Left & Form name/value pair \\ \verb+||+ & Left & Logical or \\ \ct{\&\&} & Left & Logical and \\ \ct{@} & & Function composition (see \pref{sec:func}) \\ \verb+|+ & Left & Bitwise or \\ \rtp{} & Left & Bitwise exclusive or \\ \ct{\&} & Left & Bitwise and \\ \hline \ct{==} & Left & Equal to\\ \ct{!=} & & Not equal to\\ \ct{===} & & Pointer equal to\\ \ct{!==} & & Pointer not equal to\\ \hline \ct{<} & Left & Less than \\ \ct{<=} & & Less than or equal to\\ \ct{>} & & Greater than \\ \ct{>=} & & Greater than or equal to\\ \hline \ct{<<} & Left & Left shift \\ \ct{>>} & & Right shift \\ \hline \ct{+} & Left & Addition \\ \ct{-} & & Subtraction \\ \ct{*} & Left & Multiplication \\ $/$ & & Division \\ \ct{\%} & & Remainder after division \\ \ct{!} & Left & Logical negation \\ \verb+~+ & & One's complement \\ \ct{++} & & Join (see \pref{sec:listsyntax}) \\ \verb+--+ & & Difference (see \pref{sec:listsyntax}) \\ \ct{-} & & Unary minus \\ \ct{+} & & Unary plus \\ \ct{(}\emph{type}\ct{)} & & Type cast expression \\ \ct{**} & Right & Raise to power \\ \ct{:} & & List CONS (see \pref{sec:listsyntax}) \\ \emph{space} & Left & Function application \\ \ct{?} & Left & List index (see \pref{sec:listsyntax}) \\ \ct{.} & Left & Class project (see \pref{sec:class}) \\ \hline \end{tabular} \end{center} \caption{\nip{} operators in order of increasing precedence} \mylabel{tb:precedence} \end{tab2} \subsection{The real type} \nip{} has a single number type for integers and real numbers. All are represented internally as 64-bit floating point values. You can use the four standard arithmetic operators (\ct{+}, \ct{-}, \ct{*}, \ct{/}), remainder after integer division (\%), raise-to-power (\ct{**}), the relational operators (\ct{<}, \ct{<=}, \ct{>}, \ct{>=}, \ct{==}), the bitwise logical operators (\ct{\&}, \verb+|+, \rtp{}, \verb+~+), integer shift operators (\ct{<<}, \ct{>>}) and unary negation and positive (\ct{-}, \ct{+}). Other mathematical functions are pre-defined for you: \ct{sin}, \ct{cos}, \ct{tan}, \ct{asin}, \ct{acos}, \ct{atan}, \ct{log}, \ct{log10}, \ct{exp}, \ct{exp10}, \ct{ceil}, \ct{floor}. Each has the standard behaviour. You can use type-casts on reals. However, they remain 64-bit floating point, the range is simply clipped. Casting to \ct{unsigned short} produces a 64-bit float whose fractional part has been set to zero, and which has been clipped to the range 0 to 65535. This may or may not cause rounding problems. You can write hexadecimal number constants as \verb"0xff". \subsection{The complex type} Complex numbers are rather sketchily implemented. They are generally handy for representing vectors and coordinates rather than for doing arithmetic, so the range of operations is limited. Complex constants are written as two numbers enclosed in round brackets and separated by a comma. You can use the four standard arithmetic operators (\ct{+}, \ct{-}, \ct{*}, \ct{/}), raise-to-power (\ct{**}), and unary negation and positive (\ct{-}, \ct{+}). You can use \ct{==} only of the relational operators. You can mix complex and real numbers in expressions. You can cast reals to complex and back. Use the functions \ct{re} and \ct{im} to extract the real and imaginary parts. \begin{verbatim} (12, 13) + 4 == (16, 13) (12, 2 + 2) == (12, 4) re (12, 13) == 12 im (12, 13) == 13 \end{verbatim} \subsection{The character type} Character constants are written as single characters enclosed in single quotes. You can use the relational operators (\ct{<}, \ct{<=}, \ct{>}, \ct{>=}, \ct{==}) to sort characters by ASCII order. You can cast a character to a real to get its ASCII value. You can cast a real ASCII value to a character. You can use the standard C escapes to represent non-ASCII characters. \begin{verbatim} (int) 'A' == 65 (char) 65 == 'A' is_digit x = '0' <= x && x <= '9' newline == '\n' \end{verbatim} \subsection{The boolean type} The two boolean constants are written as \ct{true} and \ct{false}. Boolean values are generated by the relational operators. You can use the standard logical operators (\ct{\&\&}, \verb+||+, \ct{!}). You can use a boolean type as an argument in an \ct{if-then-else} expression. As with C, the logical operators do not evaluate their right-hand sides if their value can be determined just from evaluating their left-hand sides. \begin{verbatim} true && false == false true || error "boink!" == true if true then 12 else 13 == 12 \end{verbatim} \subsection{The list type} \mylabel{sec:listsyntax} Lists are created from two constructors. \ct{[]} denotes the empty list. The list construction operator (\ct{:}, pronounced CONS by LISP programmers) takes an item and a list, and returns a new list with the item added to the front. As a convenience, \nip{} has a syntax for list constants. A list constant is a list of items, separated by commas, and enclosed in square brackets: \begin{verbatim} 12:[] == [12] 12:13:14:[] == 12:(13:(14:[])) == [12,13,14] [a+2,3,4] == (a+2):3:4:[] [2]:[3,4] == [[2],3,4] \end{verbatim} Use the functions \ct{hd} and \ct{tl} to take the head and the tail of a list: \begin{verbatim} hd [12,13,14] == 12 tl [12,13,14] == [13,14] \end{verbatim} Use \ct{..} in a list constant to define a list generator. List generators build lists of numbers for you: \begin{verbatim} [1..10] == [1,2,3,4,5,6,7,8,9,10] [1,3..10] == [1,3,5,7,9] [10,9..1] == [10,9,8,7,6,5,4,3,2,1] \end{verbatim} \noindent List generators are useful for expressing iteration. Lists may be infinite: \begin{verbatim} [1..] == [1,2,3,4,5,6,7,8,9 ..] [5,4..] == [5,4,3,2,1,0,-1,-2,-3 ..] \end{verbatim} \noindent Infinite lists are useful for expressing unbounded iteration. See \pref{sec:lazy}. You can write list comprehensions like this: \begin{verbatim} [x :: x <- [1..]; x % 2 == 0] \end{verbatim} \noindent This could be read as {\em All x such that x is in \verb+[1..]+ and x is even}, that is, the list of even numbers. You can have any number of semicolon-separated qualifiers and each one can be either a generator (like \verb"x <- [1..]") introducing a new variable or pattern (see \pref{sec:pattern}), or a predicate (like \verb"x % 2 == 0") which filters the generators to the left of it. Later generators change more rapidly, so for example: \begin{verbatim} [(x, y) :: x <- [1..3]; y <- [x..3]] == [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)] \end{verbatim} You can nest list comprehensions to generate more complex data structures. For example: \begin{verbatim} [[x * y :: x <- [1..10]] :: y <- [1..10]] \end{verbatim} \noindent will generate a times-table. You can use pattern-matching (see \pref{sec:pattern}) to loop over several generators at the same time. For example: \begin{verbatim} [(x, y) :: [x, y] <- zip2 [1..3] [1..3]] == [(1, 1), (2, 2), (3, 3)] \end{verbatim} As a convenience, lists of characters may be written enclosed in double quotes: \begin{verbatim} "abc" == ['a','b','c'] \end{verbatim} You can define a string constant which has the same form as a variable name (that is, letters, numbers, underscore and apostrophy only) with a \verb+$+ prefix. For example: \begin{verbatim} $form7 == "form7" \end{verbatim} \noindent \nip{} often uses these in option lists. You can define a name, value pair with the \ct{=>} operator. \begin{verbatim} $fred => 12 == ["fred", 12] \end{verbatim} \noindent Again, these pairs are frequently used to pass options to objects. A list may contain any object: \begin{verbatim} [1,'a',true,[1,2,3]] \end{verbatim} \noindent Mixing types in a list tends to be confusing and should be avoided. If you want to group a set of diverse objects, define a class instead, see \pref{sec:class}. Lists of lists of reals are useful for representing arrays. You can use the list index operator (\ct{?}) to extract an element from a position in a list: \begin{verbatim} [1,2,3] ? 0 == 1 "abc" ? 1 == 'b' \end{verbatim} You can use the list join operator (\ct{++}) to join two lists together end-to-end. \begin{verbatim} [1,2,3] ++ [4,5,6] == [1,2,3,4,5,6] \end{verbatim} You can use the list difference operator (\verb+--+) to remove elements of one list from another. \begin{verbatim} [1..10] -- [4,5,6] == [1,2,3,7,8,9,10] \end{verbatim} \subsection{The function type} \mylabel{sec:func} Functions are objects just like any other. You can pass functions to other functions as parameters, store functions in lists, and so on. You can create anonymous functions with \verb"\" (lambda). For example: \begin{verbatim} map (\x x + 2) [1..3] == [3, 4, 5] \end{verbatim} You can nest lambdas to make multi-argument anonymous functions, for example: \begin{verbatim} map2 (\x\y x + y) [1..3] [2..5] == [3, 5, 7] \end{verbatim} You can compose functions with the \ct{@} operator. For example, for two functions of one argument \ct{f} and \ct{g}: \begin{verbatim} f (g 2) == (f @ g) 2 \end{verbatim} \subsection{The image type} These represent a low-level handle to a VIPS image structure. You can make them with the \ct{vips\_image} builtin, and you can pass them as parameters to VIPS functions. The \ct{Image} class is built on top of them, see \pref{sec:Image}. As an accident of history, \nip{} also lets you do arithmetic with them. This will probably be removed in the next version or two, so it's best to go through the higher-level \ct{Image} class. \section{Lists and recursion} \mylabel{sec:lists} Functional programming languages do not have variables, assignment or iteration. You can achieve the same effects using just lists and recursion. There are two main sorts of recursion over lists. The first is called \emph{mapping}: a function is applied to each element of a list, producing a new list in which each element has been transformed. \begin{verbatim} map fn [a,b,c] == [fn a, fn b, fn c] \end{verbatim} The second main sort of recursion is called \emph{folding}: a list is turned into a single value by joining pairs of elements together with a function and a start value. \begin{verbatim} foldr fn start [a,b .. c] == (fn a (fn b (.. (fn c start)))) \end{verbatim} \noindent (The function is called \ct{foldr} as it folds the list up right-to-left. There is an analogous function called \ct{foldl} which folds a list up left-to-right, but because of the way lists work, it is much slower and should be avoided if possible.) \ct{map} is defined in the standard list library for you: \begin{verbatim} /* map fn l: map function fn over list l */ map fn l = [], l == [] = fn (hd l) : map fn (tl l) \end{verbatim} \noindent So, for example, you could use \ct{map} like this: \begin{verbatim} map (add 2) [1..5] == [3,4,5,6,7,8] \end{verbatim} \ct{foldr} is defined in the standard list library for you: \begin{verbatim} /* foldr fn st l: fold up list l, * right to left with function fn and * start value st */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)) \end{verbatim} \noindent So, for example, you could use \ct{foldr} like this: \begin{verbatim} foldr add 0 [1..5] == 15 \end{verbatim} \noindent (Mathematically, \ct{foldr} is the more basic operation. You can write \ct{map} in terms of \ct{foldr}, but you can't write \ct{foldr} in terms of \ct{map}.) Unconstrained recursion over lists can be very hard to understand, rather like \ct{goto} in an imperative language. It's much better to use a combination of \ct{map} and \ct{foldr} if you possibly can. The toolkit \ct{\_list} contains definitions of most of the standard list-processing functions. These are listed in \tref{tb:list}. Check the source for detailed comments. \begin{tab2} \begin{center} \begin{tabular}{||l|l||} \hline Name & Description \\ \hline \ct{all l} & and all the elements of list \ct{l} together \\ \ct{any l} & or all the elements of list \ct{l} together \\ \ct{concat l} & join a list of lists together \\ \ct{drop n l} & drop the first \ct{n} elements from list \ct{l} \\ \ct{dropwhile fn l} & drop while \ct{fn} is true \\ \ct{extract n l} & extract element \ct{n} from list \ct{l} \\ \ct{filter fn l} & all elements of \ct{l} for which \ct{fn} holds \\ \ct{foldl fn st l} & fold list \ct{l} left-to-right with \ct{fn} and \ct{st} \\ \ct{foldl1 fn l} & like \ct{foldl}, but use the first element of the list as the start value \\ \ct{foldr fn st l} & fold list \ct{l} right-to-left with \ct{fn} and \ct{st} \\ \ct{foldr1 fn l} & like \ct{foldr}, but use the first element of the list as the start value \\ \ct{index fn l} & search list \ct{l} for index of first element matching predicate \ct{fn} \\ \ct{init l} & remove last element of list \ct{l} \\ \ct{iterate f x} & repeatedly apply \ct{f} to \ct{x} \\ \ct{last l} & return the last element of list \ct{l} \\ \ct{len l} & find length of list \ct{l} \\ \ct{limit l} & find the first element of list \ct{l} equal to its predecessor \\ \ct{map fn l} & map function \ct{fn} over list \ct{l} \\ \ct{map2 fn l1 l2} & map 2-ary function \ct{fn} over lists \ct{l1} and \ct{l2} \\ \ct{map3 fn l1 l2 l3} & map 3-ary function \ct{fn} over lists \ct{l1}, \ct{l2} and \ct{l3} \\ \ct{member l x} & true if \ct{x} is a member of list \ct{l} \\ \ct{mkset eq l} & remove duplicates from list \ct{l} with equality function \ct{eq} \\ \ct{postfix l r} & add element \ct{r} to the end of list \ct{l} \\ \ct{product l} & product of list l \\ \ct{repeat x} & make an infinite list of \ct{x}es \\ \ct{replicate n x} & make \ct{n} copies of \ct{x} in a list \\ \ct{reverse l} & reverse list \ct{l} \\ \ct{scan fn st l} & apply \ct{(foldr fn r)} to every initial segment of list \ct{l} \\ \ct{sort l} & sort list \ct{l} into ascending order \\ \ct{sortc fn l} & sort list \ct{l} into order by using a comparison function \\ \ct{sortpl pl l} & sort list \ct{l} by predicate list \ct{pl} \\ \ct{sortr l} & sort list \ct{l} into descending order \\ \ct{split fn l} & break list \ct{l} into sections separated by predicate \ct{fn} \\ \ct{splits fn l} & break list \ct{l} into single sections separated by predicate \ct{fn} \\ \ct{splitpl pl l} & break list \ct{l} up by predicate list \ct{pl} \\ \ct{split\_lines n l} & break list \ct{l} into lines of length \ct{n} \\ \ct{sum l} & sum list l \\ \ct{take n l} & take the first \ct{n} elements from list \ct{l} \\ \ct{takewhile fn l} & take from the front of \ct{l} while \ct{fn} holds \\ \ct{zip2 l1 l2} & zip two lists together \\ \ct{zip3 l1 l2 l3} & zip three lists together \\ \hline \end{tabular} \end{center} \caption{Functions in the standard list-processing toolkit} \mylabel{tb:list} \end{tab2} \section{Lazy programming} \mylabel{sec:lazy} \nip{}'s programming language is \emph{lazy}, that is, it delays evaluation as long as it possibly can. For example, \ct{error} is a function which immediately halts execution of your function and pops up an alert window. So: \begin{verbatim} 12 + error "wombat!" \end{verbatim} \noindent Has no value: this expression will halt with an error message. However: \begin{verbatim} false && error "lasagne!" \end{verbatim} \noindent Will evaluate to \ct{false}, since \nip{} knows after looking at the left-hand-side of \ct{\&\&} that the result must be \ct{false}, and so does not evaluate the right-hand-side. \begin{verbatim} [12, error "hot chilli!"] ? 0 == 12 \end{verbatim} \noindent This also evaluates completely, since the second element of the list is never used, and therefore never evaluates. Things become more confusing when you start calling functions, since the arguments to a function call are also not evaluated until the function needs that value. For example: \begin{verbatim} foldr (error "boink!") 2 [] == 2 \end{verbatim} \noindent Again, this evaluates successfully, since the function is never used by \ct{foldr}. \section{Pattern matching} \mylabel{sec:pattern} Any time you define a name, you can use a pattern instead. For example: \begin{verbatim} [fred, petra] = [12, 13] \end{verbatim} \noindent defines \ct{fred} to have the value 12 and \ct{petra} to have the value 13. A pattern describes the structure you are expecting for the value. When the value is computed it is matched against the pattern and, if the match is successful, the names in the pattern are bound to those parts of the value. Our example is exactly equivalent to: \begin{verbatim} temp = [12, 13]; fred = temp?0, is_list temp && is_list_len 2 temp = error "pattern match failed"; petra = temp?1, is_list temp && is_list_len 2 temp = error "pattern match failed"; \end{verbatim} \noindent where \ct{temp} is an invisible, anonymous symbol. You can pattern match on any of \nip{}'s data structures and types. You can use: \begin{description} \item[\ct{a:b}] Tests for the value being a non-empty list and then assigns \ct{a} to the head and \ct{b} to the tail. \item[\ct{(a,b)}] Tests for the value being a complex and then assigns \ct{a} to the real part and \ct{b} to the imaginary. \item[\ct{[a,b,c]}] Tests for the value being a list of length three and then assigns \ct{a}, \ct{b} and \ct{c} to the three elements. \item[\ct{($class-name$ b)}] Tests for the value being an instance of the named class, then assigns \ct{b} to that class instance. \item[\ct{$constant$}] Tests for the value being equal to that constant. Constants are things like \ct{"hello world"} or \ct{12}. \end{description} You can nest patterns in any way you like. Patterns are useful in conjunction with list comprehensions, see \pref{sec:listsyntax}. You can't use patterns in function arguments in the current version, hopefully this will added shortly. \section{The standard libraries} \nip{} comes with a lot of little utility functions. The functions for list processing are listed in \tref{tb:list}. There are a huge number more, too many to really list here. \tref{tb:toolkits} lists all the utility toolkits with some hints about the kinds of function they contain. Read the (heavily commented) toolkits for details. \begin{tab2} \begin{center} \begin{tabular}{||l|l|l||} \hline Toolkit & Contains & Description \\ \hline \ct{\_convert} & \ct{parse\_int l}, \ldots{} & convert ascii text to numbers \\ & \ct{to\_matrix x}, \ldots{} & convert anything into a matrix \\ & \ct{colour\_transform\_to to x}, \ldots{} & convert between colour spaces \\ \hline \ct{\_generate} & \ct{image\_new w h ...} & make a blank image \\ & \ct{image\_white i} & look at image \ct{i}, try to guess what white is \\ & \ct{make\_xy w h} & make an image of size \ct{w} by \ct{h} whose pixel value are \\ & & their coordinates \\ \hline \ct{\_types} & \ct{Image i} & all the standard classes and support functions, \\ & & see \pref{sec:object} \\ \hline \ct{\_predicate} & \ct{is\_colour\_space i} & test for objects are in various categories or have \\ & & various properties \\ \hline \ct{\_stdenv} & \ct{logical\_and x}, \ldots{} & function versions of all the operators \\ & \ct{bandsplit i}, \ldots{} & break up and recombine images by band \\ & \ct{mean x}, \ldots{} & statistical ops on objects \\ & \ct{transpose x}, \ct{flipud x}, \ct{rot90 x}, \ldots{} & flips, rotates, etc. on objects \\ & \ct{rad x}, \ct{pi}, \ldots{} & trigonometry stuff \\ & \ct{sign x}, \ct{conj x}, \ct{polar x}, \ldots{} & complex stuff \\ & \ct{rint x}, \ct{ceil x}, \ldots{} & various rounding things \\ & \ct{fwfft x}, \ldots{} & fourier stuff \\ & \ct{dilate m x}, \ct{rank w h n i}, \ldots{} & morphology stuff \\ & \ct{conv m x}, \ldots{} & convolution stuff \\ & \ct{image\_set\_type t i}, \ldots{} & set various image header field \\ & \ct{resize x y i}, \ldots{} & resampling images \\ & \ct{recomb m i}, \ldots{} & recombinations \\ & \ct{clip2fmt f i}, \ldots{} & format conversions \\ & \ct{hist\_find m x}, \ldots{} & histogram stuff \\ & \ct{id x}, \ct{const x y}, \ldots{} & various useful operations on functions \\ & \ct{map\_binary fn x y}, \ldots{} & mapping over groups \\ \hline \end{tabular} \end{center} \caption{Useful utility functions --- see the source for details} \mylabel{tb:toolkits} \end{tab2} \section{Classes} \mylabel{sec:class} You can define new types using \ct{class}. For example: \begin{verbatim} Pasta_plain = class { lasagne = "large sheets"; fusilli = "sort of twisty"; radiatori = "lots of ridges"; } \end{verbatim} \noindent This defines a new class called \ct{Pasta\_plain}. The class has three members (\ct{lasagne}, \ct{fusilli} and \ct{radiatori}), each of which has a list of \ct{char} as its value. By convention, we've named classes with an initial capital letter, but of course you can do what you like. You can refer to the members of a class using the class project (\ct{.}) operator. For example: \begin{verbatim} Pasta_plain.lasagne == "large sheets" \end{verbatim} \noindent You can use an expression to the right of \ct{.} if you enclose it in brackets. For example: \begin{verbatim} Pasta_plain.("las" ++ "agne") == "large sheets" \end{verbatim} Classes can contain any objects as members, including functions and sub-classes. Functions may define local classes, classes may define local functions, and all may refer to each other using the usual scope rules. For example: \begin{verbatim} Pasta_all = class { filled = class { tortelloni = "venus' navel"; ravioli = "square guys"; } plain = Pasta_plain; } \end{verbatim} When you define a class, \nip{} adds a few extra members for you. \ct{name} is a list of \ct{char} giving the name of the class. \ct{this} and \ct{super} are the most-enclosing class instance and the class instance this class is derived from (see \pref{sec:inheritance}). \nip{} also adds a default constructor: a member with the same name as the class, pointing back to the class constructor. For efficiency reasons \nip{} does not allow mutual recursion at the top level. If two functions depend on each other, neither will ever be calculated. For example: \begin{verbatim} a = 1 : b; b = 2 : a; \end{verbatim} \noindent Neither \ct{a} nor \ct{b} will have a value. You can have mutual recursion between class members. For example: \begin{verbatim} Fred = class { a = 1 : b; b = 2 : a; } \end{verbatim} \noindent Now \ct{Fred.a} will have the value \ct{[1, 2, 1, 2, 1, \ldots{}]}. \subsection{Parameterised classes} Classes can have parameters. Parameters behave like class members initialised from arguments to the class constructor. For example: \begin{verbatim} My_pasta pasta_name cooked = class { is_ready t = "your " ++ pasta_name ++ " is " ++ state { state = "underdone!", t < cooked = "perfect", t == cooked = "yuk!"; } } \end{verbatim} \noindent This defines a class called \ct{My\_pasta} which takes a pasta name and a cooking time as parameters. Once you have made an instance of \ct{My\_pasta}, you can test if it's been cooked at a certain time with the \ct{is\_ready} member. For example: \begin{verbatim} tele = My_pasta "telephoni" 10; tele.is_ready 5 == "your telephoni is underdone!" \end{verbatim} \subsection{Inheritance} \mylabel{sec:inheritance} Classes can inherit from a super-class. For example: \begin{verbatim} Pasta_more = class Pasta_plain { macaroni = "tubes"; spaghetti = "long and thin"; lasagne = "fairly large sheets"; } \end{verbatim} \noindent Here the new class \ct{Pasta\_more} inherits members from the previous class \ct{Pasta\_plain}. It also overrides the definition of \ct{lasagne} from \ct{Pasta\_plain} with a new value. For example: \begin{verbatim} Pasta_more.macaroni == "tubes" Pasta_more.fusilli == "sort of twisty" Pasta_more.lasagne == "fairly large sheets" \end{verbatim} You can use \ct{this} and \ct{super} to refer to other members up and down the class hierarchy. \ct{super} is the class instance that the current class inherits from (if there's no super-class, \ct{super} has the value \ct{[]}), and \ct{this} is the most-enclosing class instance. \begin{verbatim} Pasta_more.super == Pasta_plain Pasta_more.this == Pasta_more Pasta_more.super.this == Pasta_more \end{verbatim} \noindent therefore: \begin{verbatim} Pasta_more.lasagne == "fairly large sheets" Pasta_more.super.lasagne == "large sheets" Pasta_more.super.this.lasagne == "fairly large sheets" \end{verbatim} There's a special symbol \ct{root} which encloses all symbols. For example: \begin{verbatim} fred = 12; Freddage = class { fred = 42; mystery = root.fred; } \end{verbatim} \noindent Now \ct{Fred.mystery} will have the value 12. There's another special symbol called \ct{scope} which encloses all symbols in the file this definition was loaded from. If you want to refer to another definition in the same file which is being masked somehow, use \ct{scope}. You can use the built in function \ct{is\_instanceof} to test whether an instance is or inherits from a class. For example: \begin{verbatim} is_instanceof "Pasta_more" Pasta_more == true is_instanceof "Pasta_plain" Pasta_more == true is_instanceof "Pasta_more" Pasta_plain == false \end{verbatim} The super-class constructor can take arguments, and these arguments can refer to class members. For example: \begin{verbatim} Fresh_pasta pasta_name = class My_pasta pasta_name cooked { cooked = 2; } \end{verbatim} \noindent Defines a class for fresh pasta, which always cooks in 2 minutes. You need to be careful not to make loops: if \ct{cooked} did tried to refer to something in the super-class, this class would never construct properly. \nip{} unfortunately does not check for this error. Finally, the superclass can be a fully constructed class. In this case, the superclass is cloned and the new class members wrapped around it. You can use this to write a class which can wrap any other class and add members to it. Many of the toolkit menu items use this trick to enable them to work for any object type. \subsection{Minor class features} There are a couple of other things you can do with classes. You can define a special member called \ct{\_check}. If this member is defined, then when a class instance is created, the check member is returned instead of the class itself. You can use this to implement class argument type checks, for example: \begin{verbatim} Fred a b = class { _check = this, is_real a && is_real b = error "args to Fred must " ++ "both be real" } \end{verbatim} \noindent Defines a class called \ct{Fred} which has to have two real numbers as arguments. You can define members called \ct{oo\_binary}, \ct{oo\_binary'} and \ct{oo\_unary} and do operator overloading. When \nip{} sees one of the standard operators being used on an instance of your class, it will look up one of these members and pass in the name of the operator and the argument. The two forms of the binary operator member are called for the class-on-left and the class-on-rights cases. So: \begin{verbatim} x = Fred 1 2 x + 12 == x.oo_binary "add" 12 12 + x == x.oo_binary' "add" 12 !x == x.oo_unary "negate" \end{verbatim} These two features are very primitive. The \ct{\_Object} class in the \ct{\_types} toolkit builds on these to provide a fairly high-level system for checking class arguments and defining the meaning of operators. See \pref{sec:object}. \section{Controlling the interface} \mylabel{sec:bowser} \nip{} looks at the scraps of program you type in and execute and tries to show them on the screen in a graphical way. The sorts of display you get depend on where in \nip{} you define the expression, and what sort of value it has. \subsection{Tools and toolkits} \mylabel{sec:tools} Definitions in toolkits are turned into menus off the \ct{Toolkits} menu in the main window, and added to the toolkit browser. Toolkits are loaded from files at startup or can be made in the program window. Toolkit or a definition names which start with an underscore character are hidden and not displayed. The toolkits are always displayed in alphabetical order, but you can order the items within a toolkit in any way you like. There are two ways to write toolkit definitions. Function definitions and zero-argument classes simply appear as menu items, built from static analysis of their source code. However, if a definition evaluates to an instance of the class \ct{Menu}, a menu item is built from dynamic analysis of the value of the definition. \subsubsection{Static menu items} Zero-argument classes within toolkits are displayed as pull-right menus. You can nest classes to any depth. \nip{} uses the first line of the comment before a definition as help text for that function, so it's a good idea to put a simple one-line description of the function at the start of a comment. For example, if the following text is placed in a file called \ct{Fred.def} on \nip{}'s start path, you'll get a menu in the tookits called \ct{Fred} with a pull-right and a tooltip. See \fref{fg:toolkit}. \begin{verbatim} Banana a = a * 3; Subfred = class { // add two things Jim a b = a + b; Apple e = e * 12; Harry z = 12 + z; } \end{verbatim} \begin{figure} \figw{2.5in}{toolkit.jpg} \caption{How \ct{Fred.def} will look} \mylabel{fg:toolkit} \end{figure} \subsubsection{Dynamic menu items} Dynamic menus give you much more control over the way menus are drawn and make it easy to reuse menus. A dynamic menu item is a class instance that is a sub-class of \ct{Menuitem}. It needs to have three members: \ct{label}, the text that should appear in the menu (with an underscore character to indicate the mnenonic); \ct{tooltip}, a short hint that appears as a tooltip or in the toolkit browser; \ct{icon}, an optional image file to be displayed in the menu next to the text; and \ct{action}, the function that is called when the menu item is activated. \ct{label} and \ct{tooltip} are constructor arguments for \ct{Menu}. So for example: \begin{verbatim} Wombat_find_item = class Menuitem "_Find Wombat" "analyse image and locate wombat" { icon = "nip-slider-16.png"; action x = im_wombat_locate x; } \end{verbatim} \noindent will appear as shown in \fref{fg:toolkit2}. \begin{figure} \figw{2.5in}{toolkit2.jpg} \caption{How \ct{Wombat\_find\_item} will look} \mylabel{fg:toolkit2} \end{figure} A dynamic pullright menu is a subclass of \ct{Menupullright}. It's just like \ct{Menuitem}, but without the need for an \ct{action} member. Any members which are subclasses of \ct{Menu} are displayed as items in the submenu. So again: \begin{verbatim} Wombat_item = class Menupullright "_Wombat" "wombat-related operations" { icon = "nip-slider-16.png"; item1 = Wombat_find_item; sep = Menuseparator; boink = Wombat_find_item; } \end{verbatim} \noindent will appear as shown in \fref{fg:toolkit3}. \begin{figure} \figw{2.5in}{toolkit3.jpg} \caption{How \ct{Wombat\_item} will look} \mylabel{fg:toolkit3} \end{figure} \subsection{Workspaces} \mylabel{sec:workspaces} Definitions in workspaces are displayed with \nip{}'s class browser. Each row is displayed in four main parts: a button for the row name, a line of text, a set of sub-rows for the members of the row's class, and a graphic display representing the row's value. See \fref{fg:row2}. \begin{figure} \figw{2.5in}{ir8a.jpg} \caption{Components of a workspace row} \mylabel{fg:row2} \end{figure} The text part of the right-hand-side of each row is always displayed, but the sub-rows are only displayed if the row represents a class, and the graphic is only displayed if the class is an instance of one of the classes in \tref{tb:classes}. You can subclass these if you want to use the graphic display in your own widgets. There are three separate ways to set the value for a row. You can edit the line of program text, you can edit one of the members, or you can manipulate the graphic representation (dragging a slider, or moving a region). These can be contradictory, so \nip{} resolves conflicts by always applying changes in the order text, then graphic, then member. When it applies a graphic change, \nip{} rebuilds the class using a class member called \emph{class-name}\ct{\_edit}, or if that is not defined, the class's constructor member. For example, the \ct{Colour} class can be defined as: \begin{verbatim} Colour colour_space value = class {} A1 = Colour "sRGB" [255,0,0]; \end{verbatim} \noindent There are two ways to change \ct{A1}. You can open \ct{A1} and change \ct{colour\_space} to \ct{"Lab"}, or you can double-click on the swatch and drag the disc. When you click \ct{OK} on the colour edit dialog, \nip{} searches for a member called \ct{Colour\_edit}, fails to find it, and so picks the \ct{Colour} member instead (the default constructor generated by \nip{}). It then replaces the value of A1 with [needs finishing] \begin{tab2} \begin{center} \begin{tabular}{||l|l||} \hline Class & Description \\ \hline \ct{Clock \emph{interval} \emph{value}} & A clock widget, handy for animations \\ \ct{Expression \emph{caption} \emph{expr}} & Displays an editable expression \\ \ct{Group \emph{value}} & A group of objects for iteration \\ \ct{List \emph{value}} & A list of related objects \\ \ct{Pathname \emph{caption} \emph{value}} & Displays a file browser \\ \ct{Fontname \emph{caption} \emph{value}} & Displays a font browser \\ \ct{Toggle \emph{caption} \emph{value}} & A toggle switch \\ \ct{Scale \emph{caption} \emph{from} \emph{to} \emph{value}} & A slider \\ \ct{Option \emph{caption} \emph{labels} \emph{value}} & Select one item from a list \\ \ct{Colour \emph{colour\_space} \emph{value}} & A patch of colour \\ \ct{Matrix\_vips \emph{value} \emph{scale} \emph{offset} \emph{filename} \emph{display}} & A matrix \\ \ct{Arrow \emph{image} \emph{left} \emph{top} \emph{width} \emph{height}} & Two points joined by a line on an image \\ \ct{Region \emph{image} \emph{left} \emph{top} \emph{width} \emph{height}} & A sub-area of an image \\ \ct{Plot \emph{options} \emph{value}} & Displays a plot widget \\ \ct{Image \emph{value}} & An image \\ \ct{Number \emph{caption} \emph{value}} & Displays an editable number \\ \ct{Real \emph{value}} & Displays a real number \\ \ct{Vector \emph{value}} & Displays a list of reals \\ \ct{String \emph{caption} \emph{value}} & Displays an editable string \\ \ct{Mark \emph{image} \emph{left} \emph{top}} & A point on an image \\ \ct{HGuide \emph{image} \emph{top}} & A horizontal line on an image \\ \ct{VGuide \emph{image} \emph{left}} & A vertical line on an image \\ \ct{Area \emph{image} \emph{left} \emph{top} \emph{width} \emph{height}} & A sub-area of an image, fixed in size \\ \hline \end{tabular} \end{center} \caption{\nip{} built in graphic classes} \mylabel{tb:classes} \end{tab2} \subsection{The \ct{Image} class} \mylabel{sec:Image}. say supports mixed ops with real, vector and complex constants \subsection{The \ct{Colour} class} \mylabel{sec:colour} This class displays a swatch of colour. If you double-click on the \begin{verbatim} Pathname caption value = class {} \end{verbatim} \section{The \ct{\_Object} class} \mylabel{sec:object} \section{Optimisation} \mylabel{sec:optimise} \nip{} performs three useful optimisations on expressions. First, it finds and removes common sub-expressions in functions. So for example: \begin{verbatim} if a + b < 12 then a + b else b \end{verbatim} \noindent will only evaluate \ct{a + b} once. This can save a lot of time if \ct{a} or \ct{b} is a large image. Second, \nip{} detects arithmetic operations on \ct{unsigned char} images, and replaces them with look-up tables. For example: \begin{verbatim} a = vips_image "campin.v" b = a * (a - 1) ** 0.5 \end{verbatim} \noindent Provided \ct{campin.v} is an 8 bit image image, this expression will evaluate with a single call to \ct{im\_maplut()}. Finally, \nip{} has a VIPS operation cache. It memorises the arguments to the last few hundred calls to VIPS, and the result each call gave. Before calling VIPS again, it checks to see if there is a previous call with the same arguments and if there is, uses the result it obtained last time. \section{Calling VIPS functions} \mylabel{sec:callvips} You can call any VIPS operation which has the following properties: \begin{itemize} \item There must be at least 1 output argument. If there's a single output argument, that becomes the value of the function. If there is more than one output, then the function returns a list with the outputs as members. \item The output arguments must all be one of: \begin{itemize} \item \verb+IM_TYPE_DOUBLE+, \item \verb+IM_TYPE_INT+, \item \verb+IM_TYPE_COMPLEX+, \item \verb+IM_TYPE_STRING+, \item \verb+IM_TYPE_IMAGE+, \item \verb+IM_TYPE_DOUBLEVEC+, \item \verb+IM_TYPE_DMASK+, \item \verb+IM_TYPE_IMASK+ \end{itemize} \item The input arguments must all be one of the types above, or \verb+IM_TYPE_DISPLAY+. If an argument is an input display, \nip{} passes in its current display structure, it does not take a display from your program. \end{itemize} When \nip{} starts up, it loads any VIPS plug ins it can find on its data search path. You can call functions from plug ins in just the same way. For information on writing plug ins, see the \emph{VIPS Manual}. ================================================ FILE: doc/src/reference.tex ================================================ \chapter{Reference} \mylabel{sec:reference} \noindent This chapter is supposed to be a user-interface reference. \cref{sec:menus} describes the items in the \ct{Toolkits} menu and \cref{sec:program} is the programming language reference. \cref{sec:tutorial} has a tutorial-style introduction. \section{Image view window} \mylabel{sec:view} \fref{fg:imageview} shows \nip{}'s image view window with all the toolbars turned on. If you press \ct{i} (or \ct{+}) with the keyboard focus on the image you will zoom in on the pixel your mouse pointer is over. Press \ct{o} (or \ct{-}) to zoom out again, or press the number keys \ct{1}, \ct{2}, \ct{4} and \ct{8} to jump straight to a particular magnification. If you hold down the Ctrl key while pressing these numbers, \nip{} will zoom out by that amount. If you press \ct{0} (the number zero), then \nip{} will pick a magnification or reduction which fits the image to the size of the window. When the image is too large for the window, you can use the scroll bars to move about the image. With the keyboard focus on the image the cursor keys left, right, up and down move a few pixels in each direction; hold down Shift as well to move a screenful at a time; hold down Ctrl as well to jump to the extreme edges of the image. You can also drag with the middle mouse button to pan around the image. Use the mousewheel to pan up and down, hold down Shift and the mousewheel to pan left and right. Use Ctrl and the mousewheel to zoom in and out. Use the \ct{View} menu to add extra elements to the window. You can turn the status bar on and off, and you can add a display control bar, a paintbox and a set of rulers to the window. \begin{fig2} \figw{6in}{ir2.jpg} \caption{The display control bar} \mylabel{fg:scr3} \end{fig2} If you select \ctr{View}\ctr{Toolbar}\ct{Display Control}, \nip{} will add a bar to the top of the window which you can use to change the contrast and brightness of the image you are viewing. The left-hand slider and text box set the gain for the image: each pixel is multiplied by this amount before display. The right-hand slider and text box set the offset: each pixel has this value added to it before display. This is useful for boosting the brightness in dark areas of images. \begin{figure} \figw{1.5in}{ir3.jpg} \caption{The display control bar menu} \mylabel{fg:scr4} \end{figure} If you click the left mouse button on the arrow to the left of the display control bar, \nip{} pops up a menu of useful display functions --- see \fref{fg:scr4}. \ct{Scale} searches the area of the image you are viewing for the darkest and brightest points and chooses settings for the gain and offset sliders which will stretch the image to use the full range of your screen. \ct{False colour} tries to make small differences in brightness more visible by colour-coding them. If \ct{Interpret} is turned on (it is by default), then \nip{} will look at the \ct{Type} field in the image header, and use that as a hint when transforming the image to a viewable form for you. This is usually the behaviour you want. \ct{Reset} moves the sliders back to the default positions of 1.0 and 0.0. \ct{Set As Workspace Default} makes the current display bar settings the default for all new image windows in this workspace. Finally, \ct{Hide} removes this display control bar. If you select \ctr{View}\ctr{Toolbar}\ct{Rulers}, \nip{} will add rulers to the edges of the window which you can use to measure numbers of pixels. If you left-drag from the ruler, you can create a guide. Guides are useful for lining up other things in the view window, and also affect paint box actions. A right-button menu on the rulers lets you use a mm scale rather than a pixel scale, and controls whether the \ct{Xoffset} and \ct{Yoffset} header fields are used. \mylabel{sec:paintbox} If you select \ctr{View}\ctr{Toolbar}\ct{Paint}, \nip{} adds a paint bar to the top of the window. You can use the paint bar to do simple edits to the image being displayed. See \fref{fg:paint}. While the paint bar is very limited, it does have two useful features. First, it can paint with any pixel value, even complex numbers. For example you can take the fourier transform of an image and paint out the peaks. Secondly, it doesn't operate on a memory copy of an image, it operates directly on the file on disc. This means that you can paint very quickly on images of any size, but it does make the paint bar a bit dangerous. Normally paint actions are live, that is, every time you paint something all the objects which depend on the thing you painted will recalculate. This can sometimes cause annoying delays: there's a preferences option to turn off automatic recalculations for the paint bar. \begin{fig2} \figw{6in}{ir4.jpg} \caption{The paint bar} \mylabel{fg:paint} \end{fig2} The \ct{Undo} and \ct{Redo} buttons move forward and back though paint actions. The \ct{Clear} button wipes the undo/redo history (useful if memory is getting low). There's an option in the preferences workspace which controls the number of undo steps \nip{} tracks. The slider sets the nib diameter for drawing operations, the black square is the current ink colour (you can drag and drop colour, or pixel values, from other parts of \nip2{}), and the text box at the far right is the text that will be drawn by the text tool. The paint tools will `snap' to guides, points and regions, so you can line things up easily. You can mark regions on images by holding down Ctrl and dragging down and right with the left mouse button. You can move the region about by dragging on the label with the left mouse button; you can resize it by dragging with the left mouse button in the border; you can get a useful context menu by right-clicking on the label; and you can pop up a box which will let you edit the region numerically by double-left-clicking on the label. If you drag up and left, you will make an \emph{arrow}. If you hold down Ctrl and just click the left mouse button, you will make a \emph{point}. If you drag from a horizontal or vertical ruler, you'll make a \emph{guide}. Guides are useful for lining up other things in the view window. \ctr{View}\ct{Image Header} shows the image metadata and history. Use the search box to filter large metadata sets. The \ct{File} menu contains two useful items: select \ct{Replace Image} to change the image which is being displayed in the window (you can also drag and drop new images in). Select \ct{Save Image} to save the image you are viewing to a file. See \pref{sec:loadsave} for details on \nip{}'s load and save dialogs. Use \ctr{File}\ct{New} to make regions, points, arrows and guides without the mouse. \section{File select dialogs} \mylabel{sec:loadsave} On most platforms you can drag files from your file manager directly to \nip{}'s main window. Alternatively, if you select \ctr{File}\ct{Open} in the main window, \nip{} will pop up a file dialog, see \fref{fg:open}. The open dialog has the following extra features: \begin{figure} \figw{2in}{ir5.jpg} \caption{Open dialog} \mylabel{fg:open} \end{figure} \begin{description} \item[Pin up button] Normally the file dialog closes after you have opened something. If this item is checked, the dialog will stay up instead --- this is useful if you want to load or save a series of objects. \item[Image type select] Use this menu to select the type of file you want \nip{} to display. There's a preference option to set the default image format. The VIPS file format is fast and accurate, but sadly not very widely supported (joke). You can also load and save images in TIFF, JPEG, PNG, HDR, CSV and PBM/PGM/PPM formats. You can usually load in many more formats, it depends how your \nip{} has been configured. \item[Image info] This displays a one-line summary of the selected image, plus a large thumbnail. \end{description} The save dialog adds one extra feature: \ct{Increment filename}. If pin up and increment are both selected, then after a save \nip{} will attempt to add one to the selected file name. For example, if you save a file called \ct{fred001.v}, after the save \nip{} will put the name \ct{fred002.v} into the selected file name box. Again, this is useful if you want to save a series of images. \section{Image processing window} \mylabel{sec:ipwindow} \fref{fg:startup} shows \nip{}'s main image processing window. The centre area is the workspace, the left-hand area is a pane you can reveal to write custom definitions for this workspace (see \ctr{View}\ct{Workspace Definitions}), and the right-hand pane is the toolkit browser (see \ctr{View}\ct{Toolkit Browser}). Drag with the middle mouse button to scroll the workspace window. Drop a file on to the workspace background (from your file manager) to load that file. If you right-click on the workspace background, a useful menu will appear. \begin{figure} \figw{3in}{ir7.jpg} \caption{\nip{}'s main image processing window} \mylabel{fg:startup} \end{figure} \begin{description} \item[Workspace] A workspace is split into a set of separate tabs. Right-click on a tab to get a useful menu. Press the add icon at the right to make a new tab. You can drag tabs between workspaces, or drag a tab to the desktop to make a new workspace. Use the syntax \ct{tab1.A1} to make references between tabs. \item[Tab] This area displays the current tab. Tabs are divided into columns of objects which each behave rather like windows: they can be moved around, folded away, loaded, saved and deleted. \item[Current column] One column is the current column. This is the column to which all new objects are added. Single-left-clicking on the title bar of a column makes that the current column. See \pref{sec:column}. \item[File, Edit, View] Use the \ct{File} menu to create or save work\-spaces, to open workspaces or load other objects into this workspace, to merge workspaces and to search for workspace backups. Use the \ct{Edit} menu to select, group, delete and duplicate sets of objects. Use \ct{View} to show and hide elements of the main window, and to set the object view mode. \item[Toolkits] This menu contains all of the image processing functions which are currently loaded into \nip{}. They are generally grouped by object type: all of the operations on matricies are under \ctr{Toolkits}\ct{Matrix}, for example. If you select one of these image processing operations, \nip{} will apply that operation to the bottom few items in the current column (however many are necessary --- two items for \ctr{Math}\ctr{Arithmetic}\ct{Add}, for example), or alternatively, if you have selected some objects explicitly, it will try to apply the operation to the selected objects. See \pref{sec:apply}. As you move the mouse pointer over menu items \nip{} tries to display some helpful information about the operation, including the number and type of arguments the operation expects. \item[Toolkit Browser] This side panel shows all the image processing operations again, but this time as a large flat list you can easily browse. Type into the search box at the top to filter operations by keyword. Doubleclick on an item to activate it. \item[Tab Definitions] This side pane shows private definitions for this tab. Programs you write here are loaded and saved with this workspace. See the Programming chapter for details on \nip{}'s programming language. \item[Free space] This displays the amount of disc space you have left in your temporary file area. See \pref{sec:config} if you want to change the directory \nip{} uses to store temporary files. If you left-click on the label, it changes to display the space \nip{} has free internally for performing calculations. You can change this limit in the \ct{Preferences} workspace. Click again to switch back to disc free. If you have objects selected, this area changes to show the names of the selected objects. \item[Status bar] As you move the mouse pointer about the window, this bar tries to display useful information about the thing you are pointing at. \end{description} \subsection{Columns} \mylabel{sec:column} Columns are split into a number of areas: \begin{description} \item[Column name] Each column has a name. You can pick any name you like when you make a new column with \ctr{File}\ctr{New}\ct{Column}. There's no way to rename a column, unfortunately. Objects in the column are named using the column name, plus a number. \item[Column title bar] Drag with the left mouse button held down on the column title bar to move the column around the workspace. Double-left-click on the title bar to change the comment attached to the column. Hold down the right mouse button on the column title bar to pop up a useful menu. The items in the menu let you edit the caption, select all the objects in the column, make a new column which is a copy of this column, save the column to a file, convert the column into a menu item (see \pref{sec:diaref}) and remove the whole column. \item[Column fold button] Left-clicking on the fold button folds the column away. Use this to hide columns which you still need, but which you are not interested in just now. \item[Expression entry] You can perform calculations by typing expressions directly into this box. For example, try entering the following expressions, and pressing Return: \begin{verbatim} 2 + 2 A1 + 120 "My cat likes\nlasagne" fred = 12 \end{verbatim} \noindent The last example shows custom button name creation. Normaly \nip{} will pick a name for you, but you can chose your own. \end{description} \subsection{Rows} \mylabel{sec:row} A column holds a number of rows. Each row comes in four main parts, not all of which are visible for all row values. Rows which represent classes have a pair or up/down arrows to the left of the row name button which you can use to control which parts of the row are visible. \begin{figure} \figw{1.5in}{ir8a.jpg} \caption{Components of a workspace row} \mylabel{fg:row} \end{figure} \begin{description} \item[Row name button] Each row has a name. The name is normally formed from the name of the current column, plus a number. If you double-left-click on the row name button, \nip{} will pop up a viewer or dialog box for the value of the row. If you left-click, \nip{} will select that row and deselect all other rows. If you click on an empty space in the workspace, it will deselect all rows. If you Ctrl-left-click, \nip{} will toggle selection of that row. If you select one row and then Shift-left-click on another row in the same column it will select the second row and all the rows in between. If you drag with the left button, you can change the order of rows in a column. Hold down the right mouse button for a useful menu. If you let the mouse linger over a button, a useful tooltip will appear. \item[Graphic] If the row's value is a class, and if the class is an instance of one of \nip{}'s graphic classes, then \nip{} will draw a graphic representation of the row's value. See \pref{sec:workspaces} for a more detailed explanation. \item[Members] If the row has a class for a value, then \nip{} will draw a sub-column listing the class members. Subcolumn members are in turn rows themselves. \item[Text] Finally, the text part normally shows a text representation of the row's value. If you left-click on the value, it changes to show the formula which generated that value. You can edit the formula and press Return to change it. Alternatively, selecting \ctr{View}\ct{Show Formula} toggles between displaying values for objects and displaying the formula. \end{description} \subsubsection{Object name colours} \nip{} changes the background colour of the row name button to show the state of the row. If background colours are not visible (perhaps your theme turns them off), try turning on the \ct{Display LEDs in workspace} option in \ct{Preferences}. Green means the row is selected (click on the background to unselect), red indicates an error (right-click on the row button ans select \ct{Recalculate} to see the full text of the error), brown indicates that the row value is out of date and needs recalculating and the various blues indicate parent and child relationships. \subsection{Applying operations to objects} \mylabel{sec:apply} There are three ways you can apply image processing operations to objects in your workspace: \begin{enumerate} \item Select the object you want to apply the operation to by single-left-clicking on the object name. When you single-click, the object name will change colour to show that it is selected, and \nip{} will display the name of the selected object at the left end of the status bar (this is useful if the selected object is scrolled off the edge of the window). You can select additional objects with Ctrl-left-click and Shift-left-click. This is necessary if you want to use an image processing operation that takes more than one argument. Once you have selected the rows (sometimes you need to select them in a certain order), click on the processing operation you want from the \ct{Toolkits} menu. \item If there are no objects selected when you click on an image processing operation, \nip{} uses the bottom few items (as many as are needed by the operation) in the current column. \item You can also type your formula directly into the expresion entry line at the bottom of the selected column. \cref{sec:program} describes the syntax in detail, but it's approximately C. \end{enumerate} \subsection{Batch processing} \mylabel{sec:batch} If you select a number of rows and then click \ctr{Edit}\ct{Group}, \nip{} will group the rows together. Now if you select the group and click on an item in the \ct{Toolkits} menu, \nip{} will apply that operation to every item in the group. You can group groups, and you can mix grouped and non-grouped rows freely. If you save a group, \nip{} will write each item in the group to a separate file, incrementing the filename each time. \subsection{Error handling} \mylabel{sec:error} If an object in your workspace has an error (for example, if you are trying to join two images of different types), then the object name button will turn red to show that this object contains an error and the tooltip for the button will show the error message. \subsection{Making menu items out of columns} \mylabel{sec:diaref} If you make a column that does something useful, you can make it into a menu item by following these steps: \begin{enumerate} \item Make your column look nice. Drag with the left mouse button on the object name buttons to re-order items in the column, and add comments to explain what are the input fields and what are the output. Double-click on the column title bar to add a helpful title to the column. Add a comment by typing your text (enclosed in double quotes) into the line at the bottom of the column. Left-drag the row to the right place. \item Select \ct{Make Column Into Menu Item} from the column title-bar menu, see \pref{sec:column}. This will open up a new dialog box which you can use to set a name for your new menu item and the name of the top level menu the item should be added to. \item That's it. You'll be prompted to save your new toolkit when you try to quit \nip{}. We recommend you just say \ct{OK} to the suggested location for the file. Edit your menus with the programming window, see \pref{sec:progwin}. \end{enumerate} \section{The programming window} \mylabel{sec:progwin} To pop up the programming window, click on \ctr{Toolkits}\ct{Edit Toolkits} in \nip{}'s main image processing window. The window shown in \fref{fg:Fred} should appear. Each of the things down the left of the program window is a toolkit. Each toolkit is a text file containing a set of definitions in \nip{}'s programming language. See \cref{sec:program} for details on the language. If you open a toolkit, \nip{} shows all of the definitions in that file. If you click on one of these \nip{} shows the source for that definition in the main part of the program window. After editing a definition, click on \ctr{File}\ct{Process} to make \nip{} read what you typed, compile it, and update itself. Click on \ctr{File}\ctr{New}\ct{Tool} to add a new definition to a toolkit, click on \ctr{File}\ctr{New}\ct{Toolkit} to make a completely new toolkit. You can also right-click on tools and toolkits to get a context menu, and you can left-drag tools to move them around within a toolkit or between toolkits. Some toolkits are loaded from files when \nip{} starts up, others are built from the VIPS operation database (for example, \ct{\_arithmetic}), and one (called \ct{\_builtin}) contains the functions that are built into \nip{}. If you select a tool and then click on \ctr{Help}\ct{Help on Tool}, \nip{} will try to display the relevant section from the VIPS manual in your web browser. Currently, this works only for things in the VIPS operation database: try \ctr{\_arithmetic}\ct{im\_add}, for example. There's a section in the \ct{Preferences} workspace to control which web browser \nip{} uses and how it asks for a page. Toolkits and tools whose names begin with an underscore character are not displayed in the main \ct{Toolkits} menu. The idea is that they represent little utility functions, rather than stuff a user might be interested in. See \pref{sec:tools} for more information on how tools and toolkits are displayed. You can have several programming windows open at the same time (often useful, if confusing). The \ct{Edit} menu lets you search for patterns across all definitions. The \ct{Jump To Definition} item jumps to the definition of a symbol. \mylabel{sec:trace} The \ct{Debug} menu has items which open a trace window (use this to track the actions taken by \nip{}'s reduction engine) and which report on unresolved symbols and list all current errors. \section{Command-line interface} \mylabel{sec:cmdline} You can use \nip{} from the command-line as well as from the GUI. This can be handy for automation: you can build a workspace and then run it over a whole set of images, or use \nip{} as part of a larger system. We've make websites which use \nip{} as the back-end. In command-line mode \nip{} runs without a GUI of any sort, it doesn't even need a window system to be installed on the machine. This makes it possible to use it in a server or batch context. These notes are for the Unix command-line, but they should work for Windows as well. \nip{} has three main modes of operation from the command-line: \begin{description} \item[\nip{} \emph{filename1} \ldots{}] Start \nip{} in GUI mode, loading the command-line arguments as files. Filenames can be images, workspaces, matricies, toolkits, and so on. \item[\nip{} \ct{-e} \emph{expression} \emph{arg1} \ldots{}] Start in no-GUI mode, print the value of \emph{expression}. The list \ct{argv} is set to be \verb+["filename","arg1",..]+. \item[\nip{} \ct{-s} \emph{filename} \emph{arg1} \ldots{}] Start in no-GUI mode, read in \emph{filename} as a set of definitions, print the value of symbol \ct{main}. The list \ct{argv} is set to be \verb+["filename","arg1",..]+. \end{description} You can use the \ct{-o} option to send output somewhere other than the screen. If these modes don't do quite what you need, you can get finer control of how \nip{} behaves with a set of other options: see the man page for details. \subsection{Using expression mode} The \ct{-e} option is very easy to use. For example: \begin{verbatim} nip2 -e "2 + 2" \end{verbatim} \noindent Prints 4 to stdout. \begin{verbatim} nip2 -e "99 + Image_file argv?1" -o result.png fred.jpg \end{verbatim} \noindent Loads argv1 (\ct{fred.jpg}), adds 99, writes the result to \ct{result.png}. \begin{verbatim} nip2 -e "Matrix [[1,2],[4,5]] ** -1" -o poop.mat \end{verbatim} \noindent Invert the 2x2 matrix and write the result to \ct{poop.mat}. If the result of the expression is a list, each item is printed on a new line. For example: \begin{verbatim} nip2 -e "[1..5]" \end{verbatim} \noindent Will print the numbers 1 to 5, each on a new line. If you have a list result and you are using \ct{-o} to direct the output to a file, the filename will be incremented each time you write. For example: \begin{verbatim} nip2 -e "map (add (Image_file argv?1)) [10, 20 .. 50]" -o result1.png fred.jpg \end{verbatim} \noindent Will load \ct{fred.jpg}, add 10, 20, 30, 40 and 50, then save those images to \ct{result1.png} to \ct{result5.png}. \subsection{Using script mode} With the \ct{-s} option you can use \nip{} as a Unix script interpreter. Create a file in your favourite text editor called \ct{brighten} containing: \begin{verbatim} #!/usr/bin/nip2 -s main = clip2fmt infile.format (infile * scale), argc == 3 = error "usage: infile scale -o outfile" { infile = Image_file argv?1; scale = parse_float argv?2; } \end{verbatim} \noindent The first line needs to be the path to \nip{} on your system. Use \ct{which nip2} to find the path if you don't know it. Mark the file as executable with \ct{chmod +x brighten}, then use it on one of your image files with: \begin{verbatim} brighten fred.jpg 1.5 -o bright_fred.png \end{verbatim} \noindent See \cref{sec:program} for details on the programming language. This program multiplies each input pixel by the constant, producing a floating point image, then then clips the result back to the same format as the original image (usually 8-bit unsigned). \nip{} takes a while (a few seconds) to start up, so this isn't going to be appropriate for small images or simple calculations. But for complex operations, or operations on large images, this mode can be very useful. \subsection{Using \ct{--set}} The \ct{--set} option (which can be abbreviated to \ct{-=}) lets you make changes to a workspace after loading it. Suppose the workspace \ct{test.ws} has a row called \ct{A1} with the value 12. Then entering: \begin{verbatim} nip2 test.ws --set Workspaces.test.A1=45 \end{verbatim} \noindent Will, as normal, start \nip{} and load \ct{test.ws}. But before the first recalculation, \nip{} will change the value of \ct{A1} to be 45. You can use \ct{--set} to create new symbols as well. \subsection{Other modes} A set of sub-options let you mix up other modes yourself. For example, it's common to want to run a workspace on many files. Suppose the workspace \ct{process.ws} loads an image in \ct{A1}, performs some processing and produces a result image \ct{A10}. If you run \nip{} with: \begin{verbatim} nip2 -bp \ -= 'Workspaces.process.A1=Image_file "fred.jpg"' \ -= main=Workspaces.process.A10 \ -o fred.jpg process.ws \end{verbatim} \noindent This will start \nip{} in batch (ie. no GUI) mode (the \ct{-b} switch), load \ct{process.ws}, change \ct{A1} to load another file, set \ct{main} to be the value of \ct{A10} and save the value of \ct{A10} to \ct{fred.jpg} (the \ct{-p} switch). ================================================ FILE: doc/src/tutorial.tex ================================================ \chapter{Tutorial} \mylabel{sec:tutorial} This chapter runs very quickly through how to use \nip{}'s user-interface. See \cref{sec:reference} if you want more details on how the different bits work. \section{Quick interface tour} \mylabel{sec:quicktour} Start up \nip{}. You should see something like \fref{fg:introwin} (the exact look might be different on your system). The menus at the top of the window are very ordinary, except for the \ct{Toolkits} menu which contains all of the image processing operations. The main part of the window shows a workspace. Workspaces are split into tabs (across the top) and each tab is made of a set of columns. The current column, where new objects appear, has a dark green bar across the top. Click on \ctr{File}\ct{Open} to get a file dialog and load up an image. \nip{} can load most image formats, try it and see. Check the \ct{Pin up} box to have the dialog remain after you press OK. You can also drag files from the desktop or from your file manager. After you've loaded an image, \nip{} should look like \fref{fg:loadedimage}. Double click on the thumbnail to open an image view window. Alternatively, select \ct{Edit} from the right-button menu on the thumbnail. See \fref{fg:imageview}. \begin{figure} \figw{3in}{snap2.jpg} \caption{After loading an image} \mylabel{fg:loadedimage} \end{figure} \begin{figure} \figw{3in}{snap3.jpg} \caption{Image view window} \mylabel{fg:imageview} \end{figure} As well as the standard keymappings, \nip{} has extra shortcuts for navigating images, see \tref{tb:shortcuts}. Use the \ctr{View}\ct{Toolbar} menu to turn on other features. You can have a status bar (shows image properties, mouse position and pixel value), a display control bar (lets you change scale and offset for display pixels, click on the arrow on the left for a useful extra menu), a paint bar and some rulers. \begin{tab2} \begin{center} \begin{tabular}{||l|l||} \hline Keys in image display widget & Action \\ \hline \ct{i}, \ct{+} & Zoom in on mouse pointer \\ \ct{o}, \ct{-} & Zoom out \\ Cursor up/down/left/right & Scroll a small amount in direction \\ Shift and cursor up/down/left/right & Scroll a screenful in direction \\ Ctrl and cursor up/down/left/right & Scroll to edge of image \\ Middle mouse drag & Pan image \\ Mouse wheel & Scroll up/down \\ Shift and mouse wheel & Scroll left/right \\ Ctrl and mouse wheel & Zoom in/out \\ \ct{0} (zero key) & Zoom out to fit image to window \\ \ct{1}, \ct{2}, \ct{4}, \ct{8} & Set magnification to 1, 2, 4 or 8 \\ Ctrl and \ct{2}, \ct{4}, \ct{8} & Set zoom out factor to 2, 4 or 8 \\ \hline \end{tabular} \end{center} \caption{\nip{} shortcuts for the image view window} \mylabel{tb:shortcuts} \end{tab2} You can mark things on an image. Hold down Ctrl and drag down and right with the left mouse button to mark a region. Ctrl-left-click to mark a point. Drag up and left to mark an arrow (two points connected by a line). Drag from the rulers to mark guides. Right-click on a label to get a menu which you can use to remove or edit one of these things. Left drag on a label to move the things around, left-drag on the edges or corners to resize. \fref{fg:imageviewregion} shows the same image with stuff marked on it. \begin{figure} \figw{3in}{snap4.jpg} \caption{Image view window with marked regions} \mylabel{fg:imageviewregion} \end{figure} Clean up any messing about and leave two regions on your image. The main window should now look something like \fref{fg:main2regions}. \begin{figure} \figw{3in}{snap5.jpg} \caption{Main window, two regions marked} \mylabel{fg:main2regions} \end{figure} There are three rows visible here, \ct{A1}, \ct{A4} and \ct{A7}. Each row has (from left to right) a pair of up/down arrows (these indicate that the row contains sub-rows: click on the down arrow several times to open the row up and see inside), the name button (left-click to select, Shift-left-click to extend-select, Ctrl-left-click to toggle select, click on the workspace background to unselect everything, left-drag on the name button to reorder items within the column, right-click to get a context menu) and the thumbnail image. Left-click on the name button of one of these images to select it, and then click on \ctr{Toolkits}\ctr{Image}\ctr{Transform}\ctr{Rotate}\ct{Free} (alternatively, if nothing is selected when you click on one of the toolkit menus, \nip{} will apply the operation to the bottom object in the current column). A new row will appear representing the rotation operation. Drag the slider to rotate the image (or type an angle in degrees into the box to the left of the slider and press Return). Pick an interpolation type from the option menu and zoom in on some pixels to check the result. See \fref{fg:rotate}. \begin{figure} \figw{3in}{snap6.jpg} \caption{Using \ctr{Rotate}\ct{Free}} \mylabel{fg:rotate} \end{figure} The same thing works for image processing operations that take two arguments. Left-click on one of your original regions, Ctrl-left-click on the rotated image (the box at the left of the main window status bar says what's selected and in what order), and click on \ctr{Toolkits}\ctr{Image}\ctr{Join}\ct{Left to Right}. A new row appears representing the join operation. Click on \ct{Background Colour}, type in 128 and press Return. Drag the \ct{shim} slider to 50 (or type 50 into the box just to the left of the slider and press Return). See \fref{fg:join}. \begin{figure} \figw{3in}{snap7.jpg} \caption{Using \ctr{Join}\ct{Left to Right}} \mylabel{fg:join} \end{figure} The \ct{Toolkits} menu is large and can be slow and annoying to find things in, so \nip{} has several shortcuts. First, you can tear-off menus by clicking on the dotted line at the top. If you're going to be using one of the sub-menus repeatedly this can save a lot of clicking. Next, you can set keyboard shortcuts for menu items by moving the mouse pointer over the item and pressing the key combination you want to set. Some systems won't let you edit menu shortcuts by default. For example, on GNOME, you need to enable this in \ctr{System}\ctr{Preferences}\ct{Menus \& Toolbars}. Finally, there is a toolkit browser: this shows the same set of items, but in an easier-to-browse way. Click on \ctr{View}\ct{Toolkit Browser} and the browse side-panel will appear, see \fref{fg:browse}. It shows all of the items as a single long list. Type into the search box at the top to only show items which match. Double-click (or press Return) on an item to activate it. Scroll to the right to see what arguments the item needs and what menu it appears in. \begin{figure} \figw{3in}{snap7a.jpg} \caption{The toolkit browser} \mylabel{fg:browse} \end{figure} The box at the bottom of each column is for entering new expressions. You can type stuff here, and \nip{} will make a new row for each item you enter. Try typing \ct{2 + 2} and pressing Return. The syntax is (almost, with a few small differences) the same as the C programming language. See \pref{sec:operators} for a list of the differences. Try multiplying the joined images by a small amount (eg. type something like \ct{A9 * 1.2} and press Return). Normally \nip{} will pick names for new objects for you (like \ct{A1}), but you can set a name yourself if you like. Try entering \ct{fred = 12}. Click the down button once on your brightened image and left-click on the area just below the thumbnail. You should see the stuff you typed to make that row. You can edit it to be anything else, press Return and \nip{} will recalculate. Try going back to your original image (the one you loaded from a file), open an image view window, and try dragging one of the regions. You can change any of the sliders in the rotate or the join rows as well. Right-click on a thumbnail for a useful menu. Use \ct{Save As} to save an image to a file. Use \ct{Replace From File} to change the image in a row (and recalculate the rest of your workspace, very handy). Use \ct{Header} to view an image's metadata. You can also edit the insides of objects. Click the down button next to one of your regions until the \ct{width} and \ct{height} rows appear, click on \ct{width} and type \ct{height * 2}. Now open the image window the region is defined on and try to resize it: you'll find that the width of the region is fixed, but that if you change the height, the width changes with it. This is a very general property of classes in \nip{}: you can use it to join objects together in complex ways, and to modify the behaviour of interactive objects. Right click on a column title bar to get a useful menu. Click on \ctr{File}\ctr{New}\ct{Column} make another column (handy for organising a workspace). If you drag from an image thumbnail on to the workspace background, \nip{} will make a new column for you. You can drop thumbnails on to other thumbnails to make links. There's a useful right-click menu on the tab name. Duplicating tabs is a very easy way to try something out: make a copy of your tab, try changing something, if it doesn't work out, just delete your new tab and go back to where you were. You can drag tabs between workspaces, and you can drag a tab to the desktop to make a new workspace. You can make references between tabs with the tab name and a dot, for example {\ct{tab1.A1}}. You can save columns, tabs or workspaces to workspace files, then merge them back into a colum, as part of a tab, or as a new tab. If \nip{} falls over (I do hope it doesn't), you can usually get your work back by restarting \nip{} and clicking on \ctr{File}\ct{Search for Workspace Backups}. There are a lot of preferences (perhaps too many), see \aref{sec:config}. There is a lot of stuff in the \ct{Toolkits} menus, but they do almost all have tooltips. If you let your mouse hover over a menu item for a moment you should get some helpful text. The toolkit menu is organised by object type. If you want to do something to a matrix, look in the \ctr{Toolkits}\ct{Matrix} menu. The exception is \ctr{Toolkits}\ct{Tasks} which repeats many of the regular toolkit items, but groups them by typical tasks instead. Operations can work on groups as well as on single images, so you can batch things up. If you save a group of images, \nip{} will number each image sequentially for you. You can use \ctr{Edit}\ct{Duplicate} to make copies of objects. If you select lots of objects and duplicate them, \nip{} will (fairly intelligently) rename everything for you so it all still works. \section{\nip{} for reflectogram mosaics} \mylabel{sec:irtut} This section quickly builds an infrared reflectogram mosaic using the sample images that come with \nip{}. See \cref{sec:ir} for detailed coverage. Click on \ctr{File}\ct{Open Examples}. You should see a directory called \ct{1\_point\_mosaic}. Doubleclick and you'll see a file called \ct{1pt\_mosaic.ws}. Doubleclick that and you'll load the workspace for this example. If you'd rather make the workspace yourself, click on the file type filter and select \ct{All files}. A set of 8 images should appear. Click on the first file, shift-click on the last, and click \ct{Open}. This will create a group of all eight images. Right-click on the row and select \ct{Ungroup} to unpack to a set of rows. See \fref{fg:loadsamples}. \begin{figure} \figw{3in}{snap14.jpg} \caption{Loading the sample images} \mylabel{fg:loadsamples} \end{figure} The images have been named to match their positions in the mosaic, so for example \ct{cd2.1.jpg} is the first image in row two. Open up viewing windows for the first two images by double clicking on the thumbnails. Move the two opened images viewers so that they are side by side. Adjust the zoom (using the \ct{i} and \ct{o} keys) and the pan (by dragging with the middle mouse button) so that the overlap area is visible in both images. Mark a tie-point on each image by Ctrl-left-clicking on a feature you can see in both images, see \fref{fg:readyjoin}. Move a point after you've marked it by dragging on the label. You don't need to be exact: \nip{} just uses the point you select as the start point for a search. It can cope with misses of up to about 10 pixels. To mosaic the two images together, click on \ctr{Toolkits}\ctr{Tasks}\ctr{Mosaic}\ctr{One Point}\ct{Left to Right}. See \fref{fg:joined}. \begin{figure} \figw{3in}{snap15.jpg} \caption{Ready to join} \mylabel{fg:readyjoin} \end{figure} \begin{figure} \figw{3in}{snap16.jpg} \caption{Joined images} \mylabel{fg:joined} \end{figure} Picking items deep in the toolkit menu is fiddly, so \nip{} has several shortcuts. First, you can tear off any toolkit menu by clicking on the dotted line at the top. Secondly, you can assign any keyboard accelerator to any menu item. Navigate to the menu item and while it is selected, press the key combination you want to use as a shortcut (for example, Ctrl-L might be good for \ctr{Mosaic}\ctr{One Point}\ct{Left to Right}). Now whenever you press Ctrl-L with the keyboard focus in the main window, you will do a left-right mosaic join. Finally, you can use the toolkit browser to display a selection of the tools in a pane on the right-hand side of the main window. Click on \ctr{View}\ct{Browse Toolkits}, then type ``mosaic'' into the search box at the top. The toolkit browser will display all items related to mosaicing. Double-click an item to do that action. Some systems won't let you edit menu shortcuts by default. For example, on GNOME, you need to enable this in \ctr{System}\ctr{Preferences}\ct{Menus \& Toolbars}. Join the rest of the pairs of sample images together left-right. Once you have made all the rows, join the rows together in turn to make the complete image using \ctr{Mosaic}\ctr{One Point}\ct{Top to Bottom}. When you've built the whole thing you'll see that there are differences in brightness between the tiles that make up your composite image. You can fix most problems like this automatically by selecting your final mosaiced image and clicking on \ctr{Mosaic}\ct{Balance}. This operation takes your mosaic apart, examines the overlap areas for differences in brightness, calculates a set of adjustment factors to minimise these differences, and then rebuilds the mosaic. There can be some problems left even after mosaic balance. Use \ctr{Mosaic}\ct{Tilt Brightness} to remove any left-right or up-down graduations in brightness. Save your mosaic workspace for future reference by clicking on \ctr{File}\ct{Save Workspace}. To save just the mosaiced image, right click on the thumbnail and select \ct{Save As}. \section{\nip{} for nerds} \mylabel{sec:nerdtour} This section sprints through a bit of \nip{} programming, see \pref{sec:program} for full details and a more formal definition of the language. The insides of \nip{} are built with \nip{}'s own programming language. It's a pure lazy functional language with classes. It's C's expression syntax (more or less) plus approximately Miranda/Haskell function syntax, plus some basic class stuff. \nip{}'s main window is a class browser for this programming language. Click on \ctr{Toolkits}\ct{Edit Toolkits} in \nip{}'s main window to pop up the programming window (see \pref{sec:progwin} for details on all the bits in the window), then in the edit area there type: \begin{verbatim} // add two things Fred a b = class { sum = a + b; } \end{verbatim} This defines a class called \ct{Fred} whose constructor takes two arguments, \ct{a} and \ct{b}. There's one member, called \ct{sum}, which is \ct{a} and \ct{b} added together. In the program window, click \ctr{File}\ct{Process}. This makes \nip{} read what you typed, parse it, compile it and update itself. The program window should now look like \fref{fg:Fred}. \begin{figure} \figw{3in}{snap8.jpg} \caption{Programming \ct{Fred}} \mylabel{fg:Fred} \end{figure} If you look back at the main \nip{} window, a new menu will have appeared under \ct{Toolkits} called \ct{untitled}. If you click on that, there will be a menu item called \ct{Fred}. Let your mouse linger, and you'll see a tooltip too. In the main window, type \ct{Fred 2 3} into the box at the bottom of the current column. Press Return and \nip{} will make a \ct{Fred} for you. Click on the down arrow to the left of your new \ct{Fred} once to see the members of \ct{Fred} (just \ct{sum} in this case), click again to see the class parameters too. The main window should look like \fref{fg:mainFred}. \begin{figure} \figw{3in}{snap9.jpg} \caption{Main window \ct{Fred}} \mylabel{fg:mainFred} \end{figure} Click to the right of \ct{b}, type in a new value and press Return. The \ct{sum} member should update. \nip{} keeps track of dependencies between rows, but it also tracks dependencies inside rows, both ones that come from the class, and ones created by any edits you do to the class instance after creating it. You won't see it in a simple example, but \nip{} also discovers and tracks dependencies which can arise at run time. Click on the text just to the right of the \ct{b} button again, type \ct{a} and press Return. Now edit \ct{a}: press Return and both \ct{b} and \ct{sum} will update. You can use \ct{Fred} to add any two things together. Click on \ctr{Toolkits}\ctr{Widgets}\ct{Scale} to make a scale widget, press Ctrl-U (the keyboard shortcut for \ctr{Edit}\ct{Duplicate}) to duplicate it, and finally click on \ctr{Toolkits}\ctr{untitled}\ct{Fred}. Open up the new \ct{Fred} and try dragging some of the scales around. The main window will look like \fref{fg:slideFred}. \begin{figure} \figw{3in}{snap10.jpg} \caption{Scale \ct{Fred}} \mylabel{fg:slideFred} \end{figure} The scales are classes too (instances of \ct{Scale}). You can open them up and do strange things with them as well. Open up one of the scales you made (eg. \ct{A2} in \fref{fg:slideFred}) and change the \ct{from} parameter to be \ct{A3.value}. Now try dragging the sliders again. Try dragging the \ct{sum} slider. Now go back and drag one of the original sliders. You'll see that \ct{sum} no longer updates, it's stuck at the last position you dragged it to. This is because there are now two things affecting the value of \ct{sum}: the underlying code (the \ct{a + b} inside \ct{Fred}), and the position you dragged the slider representing \ct{sum} to. \nip{} has the rule that graphical edits (dragging the slider) override code. To make \ct{sum} update again, right click on the \ct{sum} button and select \ct{Reset} from the pop up menu. Now drag one of the input sliders again, and \ct{sum} will start updating once more. Classes can inherit from other classes. Go back to the program window, click on \ctr{File}\ctr{New}\ct{Tool} to clear the edit window, and type: \begin{verbatim} // multiply two things Jim a b = class Fred a b { product = a * b; } \end{verbatim} This defines a class called \ct{Jim} which inherits from \ct{Fred}. Click \ctr{File}\ct{Process}, then back in the main window, type \ct{Jim 4 5} into the bottom of the column. Click down once to expose the members (just \ct{product}), click again to expose the parameters as well (\ct{a} and \ct{b}), and click a third time to expose the superclass member (which should be an instance of \ct{Fred}). You can also open up the \ct{super} member and see inside the \ct{Fred} that this \ct{Jim} is using as its superclass. \ct{A5} will respond to both \ct{product} and \ct{sum}. See \fref{fg:Jim}. \begin{figure} \figw{3in}{snap11.jpg} \caption{Browsing \ct{Jim}} \mylabel{fg:Jim} \end{figure} \nip{} has about 20 different graphical classes like \ct{Scale}. Whenever a row takes a new value, \nip{} checks to see if that value is an instance of one of these special classes, and if it is, it will add a graphical element to the row display which represents that class's value. It builds the graphical part by looking inside the class for certain members (for example, the scale graphic looks for members called \ct{from}, \ct{to} and \ct{value}). When you change the graphic (maybe by dragging the scale), \nip{} rebuilds the class by looking inside for a edit member (eg. \ct{Scale\_edit}) or if that's not defined, a constructor member (eg. \ct{Scale}). You can make your own graphic widgets by subclassing \nip{}'s built-in ones. By selectively overriding default constructors and adding edit members, you can control how your new widget will behave in expressions, and how it will behave if it's edited graphically. Make a new column, load up an image (use \ctr{File}\ct{Open}), open an image viewer (double-click on the thumbnail), drag out two regions on it (hold down Ctrl and the left mouse button and drag down and right). Your main window should look like \fref{fg:twomoreregions}. \begin{figure} \figw{3in}{snap12.jpg} \caption{Two more regions} \mylabel{fg:twomoreregions} \end{figure} \ct{im\_insert} is a VIPS operation that puts one image inside another at an (x, y) position. VIPS operations work on VIPS images. The \ct{value} member of an \ct{Image} or \ct{Region} is the VIPS image that underlies the \nip{} row. You can use \ct{im\_insert} to make a thing to join two images together. Back in the program window, click on \ctr{File}\ctr{New}\ct{Tool} and enter: \begin{verbatim} // join two images left-right Join a b = class Image value { shim = Scale "Spacing" 0 1000 0; value = im_insert a.value b.value (a.width + shim.value) 0; } \end{verbatim} \noindent Click \ctr{File}\ct{Process}. This defines a class \ct{Join} which subclasses the \ct{Image} graphic. Now select your two regions (click on the first one, shift-click on the second) and click on \ctr{Toolkits}\ctr{untitled}\ct{Join}. Alternatively, just click \ct{Join} and it'll be given the borrom two items in the column. A new \ct{Join} row will appear. Open it up and drag the slider to set the spacing between the two joined images. Go back to the image viewer for the image file you loaded and try dragging one of the regions. \fref{fg:myjoin} shows this class in action. The thing in \ctr{Toolkits}\ctr{Image}\ctr{Join}\ct{Left to Right} is just a slightly fancier version of this. \begin{figure} \figw{3in}{snap13.jpg} \caption{Joining two images with \ct{Join}} \mylabel{fg:myjoin} \end{figure} You can change how the graphic widgets behave by subclassing them. Try: \begin{verbatim} Scale_int c f t v = class scope.Scale c f t ((int) v) { Scale = Scale_int; } \end{verbatim} \noindent This defines a new scale class called \ct{Scale\_int} which can only take integer values. The \ct{Scale = Scale\_int;} line is \ct{Scale\_int} overriding \ct{Scale}'s constructor, so that a \ct{Scale\_int} stays a \ct{Scale\_int} when you drag. Because there's a local called \ct{Scale}, \ct{Scale\_int} needs to use \ct{scope.Scale} to refer to the superclass. Here's a version of \ct{Mark} which can only be dragged in a circle. You pass it an image to display on, an xy centre position, a radius and a start angle. \begin{verbatim} Mark_circle image x y r a = class scope.Mark image _x' _y' { // get rect cods for our point _pos = (x, y) + rectangular (r, a); _x' = re _pos; _y' = im _pos; Mark i l t = this.Mark_circle i x y r a' { // vector from centre of // circle to new position u = (l, t) - (x, y); // angle of vector a' = im (polar u); } } \end{verbatim} ================================================ FILE: man/Makefile.am ================================================ SUBDIRS = man1 ================================================ FILE: man/man1/Makefile.am ================================================ man_MANS = nip2.1 EXTRA_DIST = ${man_MANS} ================================================ FILE: man/man1/nip2.1 ================================================ .TH NIP2 1 "Oct 4 2004" .SH NAME nip2 \- image processing with the VIPS library .SH SYNOPSIS .B nip2 [filename1 ...] .br .B nip2 -s filename [arg1 ...] .br .B nip2 -e expression [arg1 ...] .SH DESCRIPTION .B nip2 (for New Image Processing) is a tool for manipulating images using the VIPS image processing library. There are three principal modes: .B nip2 [filename1 ...] .br start in GUI mode, loading the named files .B nip2 -e expression [arg1 ...] .br .B nip2 --expression=EXPRESSION [arg1 ...] .br start in no-GUI mode; set main = expression, set list argv to ["filename", "arg1", "arg2", ...], set argc to length of list; print the value of symbol "main" to stdout; exit .B nip2 -s filename [arg1 ...] .br .B nip2 --script=FILENAME [arg1 ...] .br start in no-GUI mode; read in filename as a set of definitions, set list argv to ["filename", "arg1", "arg2", ...], set argc to length of list; print the value of symbol "main" to stdout; exit; useful for running nip2 as an interpreter on unix You can use .B -o to direct output to a file rather than stdout. .B -o filename .br .B --output=FILENAME .br the value of main is written to the named file. If main is a list, the filename is incremented between objects. You can use the suffix to specify the format and options to write in Other options provide finer control over startup and shutdown. If you need to do something strange, don't use -e/-s, use these in combination. .B -b .br .B --batch .br batch (ie. non-GUI) mode .B -m .br .B --no-load-menus .br don't load menus, for faster startup .B -a .br .B --no-load-args .br don't load extra command-line arguments .B -w .br .B --stdin-ws .br load stdin as a workspace .B -d .br .B --stdin-def .br load stdin as a set of definitions .B -p .br .B --print-main .br print the value of main on exit. nip2 will check for a top-level symbol called main, and also check each workspace for a main Finally some other options are useful for debugging, timing and for generating strings for internationalisation. .B -V .br .B --verbose .br produce verbose error messages: handy for debugging in batch mode .B -i .br .B --i18n .br output strings from .def files for internationalisation .B -v .br .B --version .br print version information .B -c .br .B --benchmark .br benchmark: no GUI, just start up and shut down .B -t .br .B --time-save .br time saves: after every image save a popup tells you the time the save took in seconds .B -T .br .B --test .br test: start up (including any arg processing), test for any errors, and exit with an error code if any occured. Useful for running automated tests. .B -x PREFIX .br .B --prefix=PREFIX .br set install prefix: start up as if nip2 had been installed to PREFIX. Useful for running automated tests without installing the thing. .SH EXAMPLES nip2 fred.jpg Start nip2, loading fred.jpg. nip2 -e "2 + 2" Prints 4 to stdout. nip2 -e "99 + Image_file argv?1" -o result.png fred.jpg Load argv1 (fred.jpg), add 99, output to result.png. nip2 -e "Matrix [[1,2],[4,5]] ** -1" -o poop.mat Invert the 2x2 matrix and write the result to poop.mat. .SH COPYRIGHT 2008 (c) Imperial College, London ================================================ FILE: nip2.appdata.xml ================================================ nip2.desktop CC0-1.0 GPL-2.0+ and LGPL-2.1+ nip2 Image processing spreadsheet

nip2 is a GUI for the VIPS scientific image processing library. It's a little like a spreadsheet: you create a set of formula connecting your images together, and on a change nip2 recalculates.

It can load images in most commonly used formats, but also in scientific formats such as Matlab, FITS, OpenSlide, OpenEXR, and others. nip2 is especially useful with very large images. You can easily work with multi-gigabyte images even on a modest computer.

https://raw.githubusercontent.com/libvips/nip2/master/doc/src/figs/snap1.jpg https://libvips.github.io/libvips https://github.com/libvips/nip2
================================================ FILE: nip2.desktop.in ================================================ [Desktop Entry] Name=@PACKAGE@ Comment=Image manipulation program based on VIPS Comment[it]=Programma per la manipolazione di immagini basato su VIPS Exec=nip2 %F Icon=nip2 Terminal=false Type=Application Categories=Graphics;RasterGraphics; StartupNotify=true MimeType=image/x-vips; ================================================ FILE: nip2.spec.in ================================================ Name: @PACKAGE@ Version: @VERSION@ Release: 1%{?dist} Summary: Interactive tool for working with large images Group: Applications/Multimedia License: GPLv2+ URL: https://github.com/jcupitt/nip2 Source0: https://github.com/jcupitt/nip2/releases BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: vips-devel = %{version} BuildRequires: gtk2-devel shared-mime-info gnome-icon-theme BuildRequires: flex bison intltool fftw-devel libxml2-devel gettext BuildRequires: desktop-file-utils #Requires: # description taken from Debian package %description nip2 is a graphical front end to the VIPS package. With nip2, rather than directly editing images, you build relationships between objects in a spreadsheet-like fashion. When you make a change somewhere, nip2 recalculates the objects affected by that change. Since it is demand-driven this update is very fast, even for very, very large images. nip2 is very good at creating pipelines of image manipulation operations. It is not very good for image editing tasks like touching up photographs. For that, a tool like the GIMP should be used instead. %prep %setup -q %build %configure make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT # delete doc (we will get it later with %doc) rm -rf $RPM_BUILD_ROOT%{_datadir}/doc/nip2 # malkovich?? rm -rf $RPM_BUILD_ROOT%{_datadir}/locale/malkovich # the nip2 post install hook seems to run update-mime-database, but we # need to run it in post rm -rf $RPM_BUILD_ROOT%{_datadir}/mime mkdir -p $RPM_BUILD_ROOT%{_datadir}/mime/packages cp -a nip2.xml $RPM_BUILD_ROOT%{_datadir}/mime/packages # same with desktop file rm -rf $RPM_BUILD_ROOT%{_datadir}/applications # locale stuff %find_lang nip2 # icon install -d $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/128x128/apps cp -a share/nip2/data/vips-128.png \ $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/128x128/apps/nip2.png # desktop file desktop-file-install --vendor fedora \ --dir $RPM_BUILD_ROOT%{_datadir}/applications \ nip2.desktop %post # scriptlet for icons touch --no-create %{_datadir}/icons/hicolor || : if [ -x %{_bindir}/gtk-update-icon-cache ]; then %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || : fi # scriptlet for desktop database update-desktop-database &> /dev/null || : # MIME update-mime-database %{_datadir}/mime &> /dev/null || : %postun # scriptlet for icons touch --no-create %{_datadir}/icons/hicolor || : if [ -x %{_bindir}/gtk-update-icon-cache ]; then %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || : fi # scriptlet for desktop database update-desktop-database &> /dev/null || : # MIME update-mime-database %{_datadir}/mime &> /dev/null || : %clean rm -rf $RPM_BUILD_ROOT %files -f nip2.lang %defattr(-,root,root,-) %doc doc/html doc/pdf AUTHORS ChangeLog COPYING NEWS THANKS TODO %{_bindir}/nip2 %{_bindir}/run-nip2.sh %{_datadir}/nip2 %{_mandir}/man1/nip2.1.gz %{_datadir}/icons/hicolor/*/apps/* %{_datadir}/applications/* %{_datadir}/mime/packages/nip2.xml %changelog * Sat Jun 10 2008 John Cupitt - 8.7.0 - Update URLs * Sat Jul 19 2008 Jesper Friis - 7.15.0-1 - Added this spec file from the Fedora source rpm * Sat Mar 15 2008 Adam Goode - 7.14.1-1 - New release * Mon Mar 10 2008 Adam Goode - 7.14.0-1 - New release * Sat Feb 9 2008 Adam Goode - 7.12.5-4 - GCC 4.3 mass rebuild * Wed Dec 5 2007 Adam Goode - 7.12.5-3 - Fix desktop file validation * Tue Oct 16 2007 Adam Goode - 7.12.5-2 - Rebuild for OpenEXR soname change * Fri Sep 21 2007 Adam Goode - 7.12.5-1 - New upstream release * Thu Aug 16 2007 Adam Goode - 7.12.4-1 - New upstream release - Update License tag * Wed Jul 25 2007 Adam Goode - 7.12.2-1 - New stable release 7.12 * Sat May 5 2007 Adam Goode - 7.12.0-1 - New upstream release - Update desktop file - Remove X-Fedora category * Thu Aug 31 2006 Adam Goode - 7.10.21-1 - New upstream release * Sun Aug 13 2006 Adam Goode - 7.10.20-2 - Fix location of documentation in program so help works - Semicolon-terminate Category entry in desktop file * Sat Jul 22 2006 Adam Goode - 7.10.20-1 - New upstream release - Updated for FC5 * Thu Jan 30 2003 John Cupitt 7.8.6-1 - first stab at an rpm package for nip ================================================ FILE: nip2.xml ================================================ VIPS image VIPS image ================================================ FILE: po/ChangeLog ================================================ + en_UK translation ================================================ FILE: po/LINGUAS ================================================ ================================================ FILE: po/POTFILES.in ================================================ src/conversionview.c src/vector.c src/tslider.c src/fontnameview.c src/workspacegroup.c src/iobject.c src/plotview.c src/matrix.c src/option.c src/managed.c src/vipsobject.c src/optionview.c src/toolkitbrowser.c src/imagepresent.c src/secret.c src/paintboxview.c src/prefworkspaceview.c src/boxes.c src/toggleview.c src/columnview.c src/lex.c src/row.c src/nipmarshal.c src/imagemodel.c src/imagedisplay.c src/sliderview.c src/colourview.c src/matrixview.c src/subcolumn.c src/iwindow.c src/real.c src/pane.c src/log.c src/heap.c src/heapmodel.c src/prefs.c src/symbol.c src/imageview.c src/parse.c src/managedgvalue.c src/conversion.c src/trace.c src/gtkutil.c src/slider.c src/action.c src/rowview.c src/column.c src/model.c src/iregiongroupview.c src/pathname.c src/panechild.c src/stringview.c src/iregion.c src/expr.c src/itext.c src/iregiongroup.c src/predicate.c src/icontainer.c src/iarrow.c src/expressionview.c src/workspace.c src/toolkitgroup.c src/iimage.c src/main.c src/toolkitview.c src/imageinfo.c src/string.c src/editview.c src/tree.c src/plotpresent.c src/dump.c src/colour.c src/number.c src/mainw.c src/popupbutton.c src/iimageview.c src/managedstring.c src/regionview.c src/idialog.c src/call.c src/class.c src/preview.c src/filemodel.c src/builtin.c src/compile.c src/pathnameview.c src/spin.c src/error.c src/tool.c src/floatwindow.c src/util.c src/view.c src/plotmodel.c src/iregionview.c src/formula.c src/valueview.c src/colourdisplay.c src/plotwindow.c src/numberview.c src/path.c src/workspaceview.c src/rhsview.c src/imageheader.c src/toggle.c src/filesel.c src/toolkitgroupview.c src/link.c src/rhs.c src/classmodel.c src/toolview.c src/toolkit.c src/watch.c src/expression.c src/clock.c src/graphwindow.c src/managedgobject.c src/workspacedefs.c src/group.c src/statusview.c src/itextview.c src/graphicview.c src/reduce.c src/doubleclick.c src/nip2-cli.c src/defbrowser.c src/plotstatus.c src/vobject.c src/plot.c src/subcolumnview.c src/value.c src/program.c src/fontname.c src/prefcolumnview.c src/managedfile.c src/cache.c src/progress.c ================================================ FILE: po/POTFILES.skip ================================================ src/parse.c src/lex.c ================================================ FILE: po/POTFILES2 ================================================ ../src/action.c \ ../src/action.h \ ../src/boxes.c \ ../src/boxes.h \ ../src/browse.c \ ../src/browse.h \ ../src/builtin.c \ ../src/builtin.h \ ../src/class.c \ ../src/class.h \ ../src/classmodel.c \ ../src/classmodel.h \ ../src/colour.c \ ../src/colour.h \ ../src/colourdisplay.c \ ../src/colourdisplay.h \ ../src/colourview.c \ ../src/colourview.h \ ../src/column.c \ ../src/column.h \ ../src/columnview.c \ ../src/columnview.h \ ../src/compile.c \ ../src/compile.h \ ../src/conversion.c \ ../src/conversion.h \ ../src/conversionview.c \ ../src/conversionview.h \ ../src/doubleclick.c \ ../src/doubleclick.h \ ../src/dump.c \ ../src/dump.h \ ../src/editview.c \ ../src/editview.h \ ../src/expr.c \ ../src/expr.h \ ../src/expression.c \ ../src/expression.h \ ../src/expressionview.c \ ../src/expressionview.h \ ../src/filemodel.c \ ../src/filemodel.h \ ../src/filesel.c \ ../src/filesel.h \ ../src/fontname.c \ ../src/fontname.h \ ../src/fontnameview.c \ ../src/fontnameview.h \ ../src/formula.c \ ../src/formula.h \ ../src/graph.c \ ../src/graph.h \ ../src/graphicview.c \ ../src/graphicview.h \ ../src/group.c \ ../src/group.h \ ../src/groupview.c \ ../src/groupview.h \ ../src/gtkutil.c \ ../src/gtkutil.h \ ../src/heap.c \ ../src/heap.h \ ../src/heapmodel.c \ ../src/heapmodel.h \ ../src/helpindex.h \ ../src/iarrow.c \ ../src/iarrow.h \ ../src/iarrowview.c \ ../src/iarrowview.h \ ../src/icontainer.c \ ../src/icontainer.h \ ../src/idialog.c \ ../src/idialog.h \ ../src/iimage.c \ ../src/iimage.h \ ../src/iimageview.c \ ../src/iimageview.h \ ../src/imagedisplay.c \ ../src/imagedisplay.h \ ../src/imageinfo.c \ ../src/imageinfo.h \ ../src/imagemodel.c \ ../src/imagemodel.h \ ../src/imagepresent.c \ ../src/imagepresent.h \ ../src/imageview.c \ ../src/imageview.h \ ../src/iobject.c \ ../src/iobject.h \ ../src/ip.h \ ../src/iregion.c \ ../src/iregion.h \ ../src/iregiongroup.c \ ../src/iregiongroup.h \ ../src/iregiongroupview.c \ ../src/iregiongroupview.h \ ../src/iregionview.c \ ../src/iregionview.h \ ../src/istring.h \ ../src/itext.c \ ../src/itext.h \ ../src/itextview.c \ ../src/itextview.h \ ../src/iwindow.c \ ../src/iwindow.h \ ../src/led.c \ ../src/led.h \ ../src/lex.l \ ../src/link.c \ ../src/link.h \ ../src/main.c \ ../src/main.h \ ../src/mainw.c \ ../src/mainw.h \ ../src/matrix.c \ ../src/matrix.h \ ../src/matrixview.c \ ../src/matrixview.h \ ../src/model.c \ ../src/model.h \ ../src/nipmarshal.c \ ../src/nipmarshal.h \ ../src/number.c \ ../src/number.h \ ../src/numberview.c \ ../src/numberview.h \ ../src/option.c \ ../src/option.h \ ../src/optionview.c \ ../src/optionview.h \ ../src/orderitem.c \ ../src/orderitem.h \ ../src/orderlist.c \ ../src/orderlist.h \ ../src/paintboxview.c \ ../src/paintboxview.h \ ../src/parse.y \ ../src/path.c \ ../src/path.h \ ../src/pathname.c \ ../src/pathname.h \ ../src/pathnameview.c \ ../src/pathnameview.h \ ../src/predicate.c \ ../src/predicate.h \ ../src/prefs.c \ ../src/prefs.h \ ../src/program.c \ ../src/program.h \ ../src/reduce.c \ ../src/reduce.h \ ../src/regionview.c \ ../src/regionview.h \ ../src/rhs.c \ ../src/rhs.h \ ../src/rhsview.c \ ../src/rhsview.h \ ../src/row.c \ ../src/row.h \ ../src/rowview.c \ ../src/rowview.h \ ../src/secret.c \ ../src/secret.h \ ../src/slider.c \ ../src/slider.h \ ../src/sliderview.c \ ../src/sliderview.h \ ../src/spin.c \ ../src/spin.h \ ../src/statusview.c \ ../src/statusview.h \ ../src/string.c \ ../src/stringview.c \ ../src/stringview.h \ ../src/subcolumn.c \ ../src/subcolumn.h \ ../src/subcolumnview.c \ ../src/subcolumnview.h \ ../src/symbol.c \ ../src/symbol.h \ ../src/toggle.c \ ../src/toggle.h \ ../src/toggleview.c \ ../src/toggleview.h \ ../src/tool.c \ ../src/tool.h \ ../src/toolkit.c \ ../src/toolkit.h \ ../src/toolkitbrowser.c \ ../src/toolkitbrowser.h \ ../src/toolkitgroup.c \ ../src/toolkitgroup.h \ ../src/toolkitgroupview.c \ ../src/toolkitgroupview.h \ ../src/toolkitview.c \ ../src/toolkitview.h \ ../src/toolview.c \ ../src/toolview.h \ ../src/trace.c \ ../src/trace.h \ ../src/tree.c \ ../src/tree.h \ ../src/tslider.c \ ../src/tslider.h \ ../src/util.c \ ../src/util.h \ ../src/view.c \ ../src/view.h \ ../src/vips_call.c \ ../src/vips_call.h \ ../src/vobject.c \ ../src/vobject.h \ ../src/watch.c \ ../src/watch.h \ ../src/wild.c \ ../src/wild.h \ ../src/workspace.c \ ../src/workspace.h \ ../src/workspacegroup.c \ ../src/workspacegroup.h \ ../src/workspaceview.c \ ../src/workspaceview.h ================================================ FILE: po/en_GB.po ================================================ # UK english translation ... just swap color for colour # Copyright (C) 2011 # This file is distributed under the same license as the nip2 package. # John Cupitt , 2011. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: nip2 7.26.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2011-07-26 12:33+0100\n" "PO-Revision-Date: 2011-07-26 12:40+0100\n" "Last-Translator: John Cupitt \n" "Language-Team: en_GB \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: ../src/action.c:87 msgid "Bad arguments." msgstr "" #. Expands to eg. 'bad args to "+", called from "fred"' #. #: ../src/action.c:102 ../src/action.c:186 msgid "Called from" msgstr "" #: ../src/action.c:108 #, c-format msgid "" "Error in binary \"%s\".\n" "left = %s\n" "right = %s\n" "%s" msgstr "" #: ../src/action.c:142 ../src/class.c:190 #, c-format msgid "Member \"%s\" not found in class \"%s\"." msgstr "" #: ../src/action.c:149 #, c-format msgid "object = %s" msgstr "" #: ../src/action.c:153 #, c-format msgid "tag = %s" msgstr "" #: ../src/action.c:158 #, c-format msgid "Reference attempted in \"%s\"." msgstr "" #: ../src/action.c:162 ../src/class.c:189 msgid "Member not found." msgstr "" #: ../src/action.c:173 ../src/builtin.c:1008 ../src/call.c:249 #: ../src/class.c:49 ../src/class.c:1027 ../src/reduce.c:898 #: ../src/reduce.c:911 msgid "Bad argument." msgstr "" #: ../src/action.c:192 #, c-format msgid "" "Error in unary \"%s\".\n" "argument = %s\n" "%s" msgstr "" #: ../src/action.c:359 ../src/action.c:377 ../src/action.c:407 msgid "Bad right hand side of '.'." msgstr "" #: ../src/action.c:369 msgid "Symbol on left hand side of '.' is not scope" msgstr "" #: ../src/action.c:411 msgid "Property not found." msgstr "" #: ../src/action.c:425 msgid "Bad left hand side of '.'." msgstr "" #: ../src/action.c:937 msgid "Division by zero." msgstr "" #: ../src/action.c:1313 ../src/action.c:1593 msgid "Unimplemented." msgstr "" #: ../src/action.c:1677 ../src/action.c:1713 ../src/action.c:1971 msgid "invoking method:" msgstr "" #: ../src/boxes.c:251 msgid "Close _without Saving" msgstr "" #. Translators: translate this to a credit for you, and it'll appear in #. * the About box. #. #: ../src/boxes.c:270 msgid "translator_credits" msgstr "" #: ../src/boxes.c:279 #, c-format msgid "About %s." msgstr "" #: ../src/boxes.c:282 #, c-format msgid "%s is an image processing package." msgstr "" #: ../src/boxes.c:286 #, c-format msgid "" "%s comes with ABSOLUTELY NO WARRANTY. This is free software and you are " "welcome to redistribute it under certain conditions, see http://www.gnu.org." msgstr "" #: ../src/boxes.c:305 msgid "Personal start folder" msgstr "" #: ../src/boxes.c:309 msgid "Homepage" msgstr "" #: ../src/boxes.c:312 msgid "Linked to VIPS" msgstr "" #: ../src/boxes.c:315 msgid "Built against VIPS" msgstr "" #: ../src/boxes.c:328 msgid "Temp files in" msgstr "" #: ../src/boxes.c:394 msgid "Help page not found." msgstr "" #: ../src/boxes.c:395 #, c-format msgid "No indexed help page found for tag \"%s\"" msgstr "" #: ../src/boxes.c:604 msgid "Search for" msgstr "" #: ../src/boxes.c:605 msgid "Case sensitive" msgstr "" #: ../src/boxes.c:607 msgid "Regular expression" msgstr "" #: ../src/boxes.c:609 msgid "Search from start" msgstr "" #: ../src/boxes.c:814 msgid "Image header fields" msgstr "" #: ../src/boxes.c:830 msgid "Image history" msgstr "" #: ../src/boxes.c:946 ../src/boxes.c:957 ../src/boxes.c:986 msgid "Unable to view help file." msgstr "" #: ../src/boxes.c:947 #, c-format msgid "Unable to open URL \"%s\", windows error code = %d." msgstr "" #: ../src/boxes.c:958 #, c-format msgid "" "Attempt to view URL with xdg-open failed\n" "%s" msgstr "" #: ../src/boxes.c:963 ../src/boxes.c:995 msgid "Browser window opened." msgstr "" #: ../src/boxes.c:965 ../src/boxes.c:997 msgid "You may need to switch desktops to see the new window." msgstr "" #: ../src/boxes.c:988 #, c-format msgid "" "Attempted to launch browser with command:\n" " %s\n" "You can change this command in Preferences." msgstr "" #: ../src/boxes.c:1029 msgid "Select Font" msgstr "" #: ../src/boxes.c:1087 msgid "Font not found." msgstr "" #: ../src/boxes.c:1088 #, c-format msgid "Font \"%s\" not found on system." msgstr "" #: ../src/boxes.c:1168 msgid "Pick a font" msgstr "" #: ../src/boxes.c:1173 msgid "Set Font" msgstr "" #: ../src/boxes.c:1219 msgid "Click to select font" msgstr "" #: ../src/builtin.c:257 msgid "Out of range." msgstr "" #: ../src/builtin.c:258 msgid "gammq arguments must be a > 0, x >= 0." msgstr "" #: ../src/builtin.c:265 msgid "Not available." msgstr "" #: ../src/builtin.c:266 msgid "No GSL library available for gammq." msgstr "" #: ../src/builtin.c:422 ../src/classmodel.c:154 ../src/classmodel.c:227 #: ../src/filemodel.c:100 ../src/filemodel.c:145 ../src/filemodel.c:357 #: ../src/filemodel.c:583 ../src/filemodel.c:624 ../src/mainw.c:754 #: ../src/mainw.c:762 ../src/model.c:313 ../src/model.c:368 #: ../src/rowview.c:456 ../src/view.c:820 msgid "Not implemented." msgstr "" #: ../src/builtin.c:423 msgid "Complex math ops not implemented." msgstr "" #: ../src/builtin.c:498 msgid "Macro error." msgstr "" #: ../src/builtin.c:681 msgid "No such type" msgstr "" #: ../src/builtin.c:682 #, c-format msgid "GType %u not found." msgstr "" #: ../src/builtin.c:905 msgid "GSL library error." msgstr "" #: ../src/builtin.c:954 #, c-format msgid "Builtin \"%s\" takes %d argument." msgid_plural "Builtin \"%s\" takes %d arguments." msgstr[0] "" msgstr[1] "" #: ../src/builtin.c:1009 #, c-format msgid "" "Argument %d to builtin \"%s\" should be \"%s\", you passed:\n" " %s" msgstr "" #: ../src/cache.c:923 ../src/call.c:144 ../src/class.c:862 msgid "You passed:" msgstr "" #: ../src/cache.c:946 ../src/util.c:191 ../src/vipsobject.c:284 msgid "VIPS library error." msgstr "" #: ../src/cache.c:949 ../src/call.c:133 #, c-format msgid "Error calling library function \"%s\" (%s)." msgstr "" #: ../src/cache.c:952 #, c-format msgid "VIPS library: %s" msgstr "" #: ../src/call.c:132 msgid "CALL library error." msgstr "" #: ../src/call.c:192 msgid "Usage:" msgstr "" #: ../src/call.c:194 #, c-format msgid "CALL operator \"%s\"" msgstr "" #: ../src/call.c:196 #, c-format msgid "%s, from package \"%s\"" msgstr "" #: ../src/call.c:201 #, c-format msgid "\"%s\" takes %d argument:" msgid_plural "\"%s\" takes %d arguments:" msgstr[0] "" msgstr[1] "" #: ../src/call.c:208 #, c-format msgid "And produces %d result:" msgid_plural "And produces %d results:" msgstr[0] "" msgstr[1] "" #. Print any flags this function has. #. #: ../src/call.c:216 msgid "Flags:" msgstr "" #: ../src/call.c:220 msgid "PIO function" msgstr "" #: ../src/call.c:222 msgid "WIO function" msgstr "" #: ../src/call.c:225 msgid "coordinate transformer" msgstr "" #: ../src/call.c:227 msgid "no coordinate transformation" msgstr "" #: ../src/call.c:230 msgid "point-to-point operation" msgstr "" #: ../src/call.c:232 msgid "area operation" msgstr "" #: ../src/call.c:235 msgid "uncacheable operation" msgstr "" #: ../src/call.c:237 msgid "operation can be cached" msgstr "" #: ../src/call.c:252 #, c-format msgid "Argument %d (%s) to \"%s\" is the wrong type." msgstr "" #: ../src/call.c:269 ../src/class.c:657 ../src/class.c:889 ../src/class.c:986 #: ../src/compile.c:1766 ../src/vipsobject.c:125 msgid "Too many arguments." msgstr "" #: ../src/call.c:272 #, c-format msgid "Too many arguments to \"%s\"." msgstr "" #: ../src/call.c:290 msgid "Unknown type." msgstr "" #: ../src/call.c:291 #, c-format msgid "CALL type \"%s\" not supported" msgstr "" #: ../src/call.c:1262 ../src/call.c:1346 #, c-format msgid "image \"%s\"" msgstr "" #: ../src/call.c:1269 msgid "no image" msgstr "" #: ../src/call.c:1300 msgid "doublevec" msgstr "" #: ../src/call.c:1342 msgid "imagevec" msgstr "" #: ../src/class.c:50 #, c-format msgid "Object %s is not a class." msgstr "" #: ../src/class.c:333 msgid "No such secret." msgstr "" #: ../src/class.c:334 msgid "" "Editing local classes which reference non-local objects is a bit broken at " "the moment :-(" msgstr "" #: ../src/class.c:658 #, c-format msgid "You can't have more than %d arguments to a superclass constructor." msgstr "" #: ../src/class.c:794 ../src/class.c:854 msgid "Bad superclass." msgstr "" #: ../src/class.c:795 #, c-format msgid "Superclass constructor \"%s\" refers to non-local symbols %s" msgstr "" #: ../src/class.c:803 ../src/workspace.c:1693 msgid "Wrong number of arguments." msgstr "" #: ../src/class.c:804 #, c-format msgid "Superclass constructor \"%s\" expects %d arguments, not %d." msgstr "" #: ../src/class.c:858 #, c-format msgid "First element in superclass of \"%s\" must be class or constructor." msgstr "" #: ../src/class.c:890 ../src/class.c:987 #, c-format msgid "" "Too many arguments to class constructor \"%s\". No more than %d arguments " "are supported." msgstr "" #: ../src/class.c:980 msgid "Class not found." msgstr "" #: ../src/class.c:981 #, c-format msgid "Class \"%s\" not found." msgstr "" #: ../src/class.c:1018 #, c-format msgid "Member \"%s\" of class \"%s\" should be of type \"%s\", instead it's:" msgstr "" #: ../src/classmodel.c:155 ../src/classmodel.c:228 #, c-format msgid "_%s() method not implemented for %s." msgstr "" #: ../src/classmodel.c:163 #, c-format msgid "Save %s \"%s\"" msgstr "" #: ../src/classmodel.c:237 #, c-format msgid "Replace %s \"%s\"" msgstr "" #: ../src/classmodel.c:695 msgid "Set boolean value here" msgstr "" #: ../src/classmodel.c:701 msgid "Enter a floating point number here" msgstr "" #: ../src/classmodel.c:708 msgid "Enter a string here" msgstr "" #. Expands to "Edit Toggle A1". #. #: ../src/classmodel.c:762 #, c-format msgid "Edit %s %s" msgstr "" #: ../src/classmodel.c:773 #, c-format msgid "Set %s" msgstr "" #: ../src/classmodel.c:1069 msgid "Unknown option." msgstr "" #: ../src/classmodel.c:1070 #, c-format msgid "Option \"%s\" not known." msgstr "" #: ../src/classmodel.c:1144 ../src/matrixview.c:189 ../src/matrixview.c:210 #: ../src/plot.c:140 ../src/plot.c:150 ../src/plot.c:360 ../src/plot.c:379 #: ../src/plot.c:392 msgid "Bad value." msgstr "" #: ../src/classmodel.c:1145 #, c-format msgid "%d band value only" msgstr "" #: ../src/clock.c:59 ../src/clock.c:93 ../src/clock.c:190 msgid "Interval" msgstr "" #: ../src/clock.c:60 ../src/clock.c:96 msgid "Elapsed time" msgstr "" #: ../src/clock.c:93 msgid "Interval between ticks (seconds)" msgstr "" #: ../src/clock.c:96 msgid "Elapsed time (seconds)" msgstr "" #: ../src/clock.c:100 #, c-format msgid "Edit Clock \"%s\"" msgstr "" #: ../src/clock.c:104 msgid "Set Clock" msgstr "" #: ../src/clock.c:193 ../src/colour.c:305 ../src/fontname.c:61 #: ../src/matrix.c:262 ../src/number.c:68 ../src/option.c:71 #: ../src/pathname.c:84 ../src/plot.c:340 ../src/slider.c:57 #: ../src/string.c:70 ../src/toggle.c:51 msgid "Value" msgstr "" #: ../src/colour.c:264 #, c-format msgid "Edit Color \"%s\"" msgstr "Edit Colour \"%s\"" #: ../src/colour.c:270 msgid "Set Color" msgstr "Set Colour" #: ../src/colour.c:302 msgid "Color Space" msgstr "Colour Space" #: ../src/colourdisplay.c:259 msgid "Double-click to edit this color, or drag-and-drop between colors" msgstr "Double-click to edit this colour, or drag-and-drop between colours" #: ../src/column.c:326 ../src/tool.c:867 ../src/workspace.c:1529 #: ../src/workspacegroup.c:145 msgid "Name clash." msgstr "" #: ../src/column.c:327 #, c-format msgid "Can't create column \"%s\". A column with that name already exists." msgstr "" #: ../src/column.c:364 msgid "Empty column." msgstr "" #: ../src/column.c:365 msgid "There are no objects in the current column." msgstr "" #: ../src/column.c:384 msgid "Too few items." msgstr "" #: ../src/column.c:385 #, c-format msgid "This column only has %d items, but %s needs %d items." msgstr "" #: ../src/columnview.c:133 #, c-format msgid "Save Column \"%s\"" msgstr "" #: ../src/columnview.c:180 ../src/columnview.c:241 ../src/mainw.c:1369 #: ../src/mainw.c:1407 ../src/program.c:596 ../src/program.c:625 #: ../src/program.c:1052 ../src/program.c:1093 ../src/program.c:1139 #: ../src/program.c:1171 ../src/program.c:1503 ../src/program.c:1544 #: ../src/program.c:2201 ../src/row.c:480 ../src/workspacegroup.c:177 #: ../src/workspacegroup.c:213 msgid "Name" msgstr "" #: ../src/columnview.c:181 ../src/columnview.c:243 msgid "Toolkit" msgstr "" #: ../src/columnview.c:182 ../src/columnview.c:245 ../src/matrix.c:271 #: ../src/program.c:597 ../src/program.c:627 ../src/program.c:1140 #: ../src/program.c:1173 msgid "Filename" msgstr "" #: ../src/columnview.c:241 msgid "Set menu item text here" msgstr "" #: ../src/columnview.c:243 msgid "Add to this toolkit" msgstr "" #: ../src/columnview.c:245 msgid "Store column in this file" msgstr "" #: ../src/columnview.c:248 #, c-format msgid "New Menu Item from Column \"%s\"" msgstr "" #: ../src/columnview.c:253 msgid "Menuize" msgstr "" #: ../src/columnview.c:648 msgid "Edit caption, press enter to accept changes, press escape to cancel" msgstr "" #: ../src/columnview.c:701 msgid "Enter expressions here" msgstr "" #: ../src/columnview.c:755 msgid "doubleclick to set title" msgstr "" #: ../src/columnview.c:764 msgid "Fold the column away" msgstr "" #: ../src/columnview.c:769 msgid "Open the column" msgstr "" #: ../src/columnview.c:893 msgid "Column menu" msgstr "" #: ../src/columnview.c:894 msgid "_Edit Caption" msgstr "" #: ../src/columnview.c:896 ../src/mainw.c:1708 ../src/program.c:1719 msgid "Select _All" msgstr "" #: ../src/columnview.c:903 msgid "Make Column Into _Menu Item" msgstr "" #: ../src/columnview.c:940 msgid "Left-drag to move, left-double-click to set title, right-click for menu" msgstr "" #: ../src/columnview.c:973 msgid "Delete the column" msgstr "" #: ../src/compile.c:1448 msgid "Too many shared nodes in graph." msgstr "" #: ../src/compile.c:1450 msgid "Raise MAX_RELOC" msgstr "" #: ../src/compile.c:1767 #, c-format msgid "Member \"%s\" of class \"%s\" should have no arguments." msgstr "" #: ../src/compile.c:2644 msgid "pattern match failed" msgstr "" #: ../src/conversion.c:473 msgid "not uncoded" msgstr "" #: ../src/conversion.c:1415 #, c-format msgid "Header for \"%s\"" msgstr "" #: ../src/conversion.c:1417 msgid "OK" msgstr "" #: ../src/conversionview.c:66 msgid "Unable to find image range." msgstr "" #: ../src/conversionview.c:67 msgid "Find image range failed." msgstr "" #: ../src/conversionview.c:90 msgid "Unable to scale image." msgstr "" #: ../src/conversionview.c:91 msgid "Maximum and minimum pixel values are equal." msgstr "" #. Build menu. One for each window, as we need to track falsecolour #. * etc. toggles. Could just have one, and modify pre-popup, but this #. * is easier. #. #: ../src/conversionview.c:229 msgid "Convert menu" msgstr "" #: ../src/conversionview.c:230 msgid "_Scale" msgstr "" #: ../src/conversionview.c:232 msgid "_False Color" msgstr "_False Colour" #: ../src/conversionview.c:234 msgid "_Interpret" msgstr "" #: ../src/conversionview.c:236 ../src/regionview.c:1020 msgid "_Reset" msgstr "" #: ../src/conversionview.c:238 msgid "Set As Workspace _Default" msgstr "" #: ../src/dump.c:186 ../src/expr.c:641 msgid "value" msgstr "" #: ../src/dump.c:187 msgid "parameter" msgstr "" #: ../src/dump.c:188 msgid "zombie" msgstr "" #: ../src/dump.c:189 ../src/util.c:1349 msgid "workspace" msgstr "" #: ../src/dump.c:190 msgid "workspace group" msgstr "" #: ../src/dump.c:191 msgid "root symbol" msgstr "" #: ../src/dump.c:192 msgid "external symbol" msgstr "" #: ../src/dump.c:193 msgid "built-in symbol" msgstr "" #: ../src/editview.c:79 ../src/fontnameview.c:80 ../src/formula.c:198 #: ../src/gtkutil.c:756 ../src/gtkutil.c:791 ../src/gtkutil.c:845 #: ../src/optionview.c:172 ../src/pathnameview.c:80 ../src/sliderview.c:74 #, c-format msgid "%s:" msgstr "" #: ../src/editview.c:158 msgid "Escape to cancel edit, press Return to accept edit and recalculate" msgstr "" #: ../src/error.c:60 msgid "No ierrors found." msgstr "" #: ../src/error.c:100 msgid "No unresolved symbols found." msgstr "" #: ../src/error.c:115 ../src/trace.c:230 msgid "_Clear" msgstr "" #: ../src/error.c:116 msgid "Clear ierror window" msgstr "" #: ../src/error.c:120 msgid "List _iErrors" msgstr "" #: ../src/error.c:121 msgid "Search for all ierrors" msgstr "" #: ../src/error.c:125 msgid "List _Unresolved" msgstr "" #: ../src/error.c:126 msgid "Search for all unresolved references" msgstr "" #: ../src/error.c:202 #, c-format msgid "iError - %s" msgstr "" #: ../src/expr.c:64 #, c-format msgid "error in \"%s\"" msgstr "" #: ../src/expr.c:346 msgid "Error" msgstr "" #: ../src/expr.c:613 msgid "top level" msgstr "" #: ../src/expr.c:618 msgid "class" msgstr "" #: ../src/expr.c:621 msgid "instance" msgstr "" #: ../src/expr.c:625 msgid "definition" msgstr "" #: ../src/expr.c:632 #, c-format msgid "parameter \"%s\"" msgstr "" #: ../src/expr.c:636 msgid "member" msgstr "" #: ../src/expr.c:645 ../src/itext.c:446 msgid "function" msgstr "" #: ../src/expr.c:654 msgid "of" msgstr "" #: ../src/filemodel.c:101 ../src/filemodel.c:146 ../src/filemodel.c:584 #: ../src/filemodel.c:625 ../src/model.c:314 ../src/model.c:369 #: ../src/model.c:388 #, c-format msgid "_%s() not implemented for class \"%s\"." msgstr "" #: ../src/filemodel.c:270 ../src/filemodel.c:285 ../src/model.c:419 msgid "XML library error." msgstr "" #: ../src/filemodel.c:271 msgid "model_save_filename: xmlNewDoc() failed" msgstr "" #: ../src/filemodel.c:286 msgid "model_save_filename: xmlNewDocNode() failed" msgstr "" #: ../src/filemodel.c:302 msgid "Save failed." msgstr "" #: ../src/filemodel.c:303 #, c-format msgid "" "Save of %s \"%s\" to file \"%s\" failed.\n" "%s" msgstr "" #: ../src/filemodel.c:358 msgid "filemodel_real_save_all: no save method" msgstr "" #: ../src/filemodel.c:469 ../src/filemodel.c:478 ../src/filemodel.c:491 #: ../src/mainw.c:1022 ../src/model.c:130 msgid "Load failed." msgstr "" #: ../src/filemodel.c:470 #, c-format msgid "Can't load XML file \"%s\", it's not a %s save file." msgstr "" #: ../src/filemodel.c:479 #, c-format msgid "" "Can't load XML file \"%s\", unable to extract version information from " "namespace." msgstr "" #: ../src/filemodel.c:492 #, c-format msgid "Can't load XML file \"%s\", the file does not contain a %s." msgstr "" #. Expands to (eg.) "Save Column A2". #. #: ../src/filemodel.c:677 #, c-format msgid "Save %s %s" msgstr "" #: ../src/filemodel.c:753 ../src/filemodel.c:765 msgid "Object has been modified." msgstr "" #: ../src/filemodel.c:754 #, c-format msgid "" "%s \"%s\" has been modified since you loaded it from file \"%s\".\n" "\n" "Do you want to save your changes?" msgstr "" #: ../src/filemodel.c:766 #, c-format msgid "%s \"%s\" has been modified. Do you want to save your changes?" msgstr "" #: ../src/filesel.c:86 msgid "Workspace files (*.ws)" msgstr "" #: ../src/filesel.c:88 msgid "Recombination matrix files (*.rec)" msgstr "" #: ../src/filesel.c:90 msgid "Morphology matrix files (*.mor)" msgstr "" #: ../src/filesel.c:92 msgid "Convolution matrix files (*.con)" msgstr "" #: ../src/filesel.c:94 msgid "Matrix files (*.mat)" msgstr "" #: ../src/filesel.c:96 msgid "Definition files (*.def)" msgstr "" #: ../src/filesel.c:98 msgid "ICC profiles (*.icc, *.icm)" msgstr "" #: ../src/filesel.c:100 msgid "All files (*)" msgstr "" #. Used as eg. "VIPS image files (*.v)" #. #: ../src/filesel.c:133 msgid "image files" msgstr "" #: ../src/filesel.c:500 #, c-format msgid "Unable to determine space free in \"%s\"." msgstr "" #. Expands to (eg.) '6GB free in "/pics/tmp"' #. #: ../src/filesel.c:511 #, c-format msgid "free in \"%s\"" msgstr "" #: ../src/filesel.c:759 ../src/util.c:1932 ../src/util.c:1938 msgid "Bad filename." msgstr "" #: ../src/filesel.c:760 msgid "No file selected." msgstr "" #: ../src/filesel.c:973 msgid "Increment filename" msgstr "" #: ../src/filesel.c:979 msgid "After Save, add 1 to the last number in the file name" msgstr "" #: ../src/filesel.c:1220 #, c-format msgid "%s Save Preferences" msgstr "" #: ../src/filesel.c:1281 msgid "Overwrite" msgstr "" #: ../src/filesel.c:1282 msgid "Overwrite file?" msgstr "" #: ../src/filesel.c:1283 #, c-format msgid "File \"%s\" exists. OK to overwrite?" msgstr "" #: ../src/fontname.c:58 ../src/mainw.c:1370 ../src/mainw.c:1409 #: ../src/number.c:65 ../src/option.c:65 ../src/pathname.c:81 #: ../src/program.c:1053 ../src/program.c:1095 ../src/slider.c:48 #: ../src/string.c:67 ../src/toggle.c:48 ../src/workspacegroup.c:178 #: ../src/workspacegroup.c:215 msgid "Caption" msgstr "" #: ../src/formula.c:143 msgid "" "Press Escape to cancel edit, press Return to accept edit and recalculate" msgstr "" #: ../src/gtkutil.c:608 msgid "Bad identifier." msgstr "" #: ../src/gtkutil.c:610 msgid "" "Enter an identifier. Identifiers start with a letter, and then contain only " "letters, numbers, apostrophy and underscore." msgstr "" #: ../src/gtkutil.c:663 ../src/gtkutil.c:671 ../src/matrixview.c:96 msgid "Bad floating point number." msgstr "" #: ../src/gtkutil.c:664 ../src/matrixview.c:97 #, c-format msgid "\"%s\" is not a floating point number." msgstr "" #: ../src/gtkutil.c:672 #, c-format msgid "Extra characters \"%s\" after number." msgstr "" #: ../src/gtkutil.c:696 msgid "Bad integer." msgstr "" #: ../src/gtkutil.c:697 #, c-format msgid "\"%s\" is not an integer." msgstr "" #: ../src/gtkutil.c:715 msgid "Bad unsigned integer." msgstr "" #: ../src/gtkutil.c:731 msgid "Bad positive integer." msgstr "" #: ../src/gtkutil.c:853 ../src/toggleview.c:106 msgid "Left-click to change value" msgstr "" #: ../src/heap.c:812 msgid "Heap full." msgstr "" #: ../src/heap.c:818 #, c-format msgid "" "The compile heap for %s has filled. Make it smaller and less complicated." msgstr "" #: ../src/heap.c:823 msgid "" "The main calculation heap has filled. Raise the heap size limit in " "Preferences." msgstr "" #: ../src/heap.c:1852 msgid "Unimplemented list type." msgstr "" #: ../src/heap.c:1947 msgid "Unimplemented type." msgstr "" #: ../src/heap.c:1948 #, c-format msgid "Unable to convert %s to a nip type." msgstr "" #: ../src/heap.c:2111 msgid "circular" msgstr "" #: ../src/heap.c:2116 #, c-format msgid "circular to label %d" msgstr "" #: ../src/heap.c:2126 #, c-format msgid "label %d" msgstr "" #: ../src/heap.c:2143 msgid "unevaluated" msgstr "" #: ../src/heap.c:2178 #, c-format msgid "class (%p)" msgstr "" #: ../src/heap.c:2189 msgid "members" msgstr "" #: ../src/heap.c:2200 msgid "secret" msgstr "" #: ../src/heap.c:2253 #, c-format msgid "no value (type %d)" msgstr "" #: ../src/heap.c:2261 msgid "NULL pointer" msgstr "" #: ../src/heap.c:2270 msgid "symbol" msgstr "" #: ../src/heap.c:2278 msgid "constructor" msgstr "" #: ../src/heap.c:2286 msgid "symref" msgstr "" #: ../src/heap.c:2294 msgid "compileref" msgstr "" #: ../src/heap.c:2322 #, c-format msgid "tag \"%s\"" msgstr "" #: ../src/heap.c:2337 #, c-format msgid "unknown element tag %d" msgstr "" #: ../src/iarrow.c:117 msgid "No image" msgstr "" #. Used in (eg.) "Mark at (10, 10) on [A1, A2]" #. #. Expands to (eg.) "Region on A1 at (10, 10), size (50, 50)" #. #: ../src/iarrow.c:130 ../src/iregion.c:156 msgid "on" msgstr "" #: ../src/iarrow.c:148 ../src/iarrow.c:153 #, c-format msgid "at %d" msgstr "" #: ../src/iarrow.c:158 #, c-format msgid "at (%d, %d)" msgstr "" #: ../src/iarrow.c:165 #, c-format msgid "at (%d, %d), offset (%d, %d)" msgstr "" #: ../src/idialog.c:432 msgid "Pin up" msgstr "" #: ../src/idialog.c:434 msgid "Check this to pin the dialog up" msgstr "" #: ../src/iimage.c:115 msgid "Original filename" msgstr "" #: ../src/iimage.c:338 msgid "Save timer." msgstr "" #: ../src/iimage.c:339 #, c-format msgid "Image save took %g seconds." msgstr "" #: ../src/imageinfo.c:676 msgid "User cancelled operation" msgstr "" #: ../src/imageinfo.c:976 msgid "Unable to open image." msgstr "" #: ../src/imageinfo.c:977 #, c-format msgid "Unable to open file \"%s\" as image." msgstr "" #: ../src/imageinfo.c:1105 msgid "Unable to write to file." msgstr "" #: ../src/imageinfo.c:1106 #, c-format msgid "Error writing image to file \"%s\"." msgstr "" #: ../src/imageinfo.c:1122 msgid "Unable to paint on image." msgstr "" #: ../src/imageinfo.c:1123 #, c-format msgid "" "Unable to get write permission for file \"%s\".\n" "Check permission settings." msgstr "" #: ../src/imageinfo.c:1167 msgid "Modify" msgstr "" #: ../src/imageinfo.c:1168 msgid "Modify disc file?" msgstr "" #: ../src/imageinfo.c:1169 #, c-format msgid "" "This image is being shown directly from the disc file:\n" "\n" " %s\n" "\n" "If you paint on this file, it will be permanently changed. If something goes " "wrong, you may lose work. Are you sure you want to modify this file?" msgstr "" #: ../src/imageinfo.c:1771 msgid "Unable to paint text." msgstr "" #: ../src/imageinfo.c:1772 #, c-format msgid "Unable to paint text \"%s\" in font \"%s\"." msgstr "" #: ../src/imagemodel.c:510 msgid "No text specified." msgstr "" #: ../src/imagemodel.c:511 msgid "Enter some text to paint in the entry widget at the top of the window." msgstr "" #. Need one menu per image window (could have a single menu for all #. * windows, but then we'd have to set the state of the toggle buttons #. * before mapping) #. #: ../src/imagepresent.c:1579 msgid "Ruler menu" msgstr "" #: ../src/imagepresent.c:1580 msgid "Rulers In _mm" msgstr "" #: ../src/imagepresent.c:1582 msgid "Show _Offset" msgstr "" #: ../src/imageview.c:457 msgid "New _Mark" msgstr "" #: ../src/imageview.c:458 msgid "Create a new mark" msgstr "" #: ../src/imageview.c:462 msgid "New _Horizontal Guide" msgstr "" #: ../src/imageview.c:463 msgid "Create a new horizontal guide" msgstr "" #: ../src/imageview.c:467 msgid "New _Vertical Guide" msgstr "" #: ../src/imageview.c:468 msgid "Create a new vertical guide" msgstr "" #: ../src/imageview.c:472 msgid "New _Arrow" msgstr "" #: ../src/imageview.c:473 msgid "Create a new arrow" msgstr "" #: ../src/imageview.c:477 msgid "New _Region" msgstr "" #: ../src/imageview.c:478 msgid "Create a new region" msgstr "" #: ../src/imageview.c:482 msgid "Replace Image" msgstr "" #: ../src/imageview.c:483 msgid "Replace image from file" msgstr "" #: ../src/imageview.c:487 msgid "Save Image As" msgstr "" #: ../src/imageview.c:488 msgid "Save image to file" msgstr "" #: ../src/imageview.c:492 ../src/mainw.c:868 msgid "Recalculate" msgstr "" #: ../src/imageview.c:493 msgid "Recalculate image" msgstr "" #: ../src/imageview.c:497 msgid "Image _Header" msgstr "" #: ../src/imageview.c:498 msgid "View image header" msgstr "" #: ../src/imageview.c:502 ../src/imageview.c:556 msgid "Zoom _In" msgstr "" #: ../src/imageview.c:503 ../src/imageview.c:557 msgid "Zoom in on mouse cursor" msgstr "" #: ../src/imageview.c:507 ../src/imageview.c:561 msgid "Zoom _Out" msgstr "" #. IMAGEMODEL_MAGIN #: ../src/imageview.c:508 ../src/imageview.c:562 ../src/paintboxview.c:249 msgid "Zoom out" msgstr "" #: ../src/imageview.c:512 msgid "Zoom _100%" msgstr "" #: ../src/imageview.c:513 ../src/imageview.c:576 msgid "Zoom to 100%" msgstr "" #: ../src/imageview.c:517 msgid "Zoom to _Fit" msgstr "" #: ../src/imageview.c:518 msgid "Zoom to fit image to window" msgstr "" #: ../src/imageview.c:524 ../src/plotwindow.c:167 msgid "_Status" msgstr "" #: ../src/imageview.c:525 ../src/plotwindow.c:168 msgid "Show status bar" msgstr "" #: ../src/imageview.c:529 msgid "_Display Control" msgstr "" #: ../src/imageview.c:530 msgid "Show display control bar" msgstr "" #: ../src/imageview.c:534 msgid "_Paint" msgstr "" #: ../src/imageview.c:535 msgid "Show paint bar" msgstr "" #: ../src/imageview.c:539 msgid "_Rulers" msgstr "" #: ../src/imageview.c:540 msgid "Show rulers" msgstr "" #: ../src/imageview.c:546 msgid "_Select" msgstr "" #: ../src/imageview.c:547 msgid "Select and modify selections" msgstr "" #: ../src/imageview.c:551 msgid "_Pan" msgstr "" #: ../src/imageview.c:552 msgid "Pan image" msgstr "" #: ../src/imageview.c:568 msgid "6%" msgstr "" #: ../src/imageview.c:568 msgid "Zoom to 6%" msgstr "" #: ../src/imageview.c:570 msgid "12%" msgstr "" #: ../src/imageview.c:570 msgid "Zoom to 12%" msgstr "" #: ../src/imageview.c:572 msgid "25%" msgstr "" #: ../src/imageview.c:572 msgid "Zoom to 25%" msgstr "" #: ../src/imageview.c:574 msgid "50%" msgstr "" #: ../src/imageview.c:574 msgid "Zoom to 50%" msgstr "" #: ../src/imageview.c:576 msgid "100%" msgstr "" #: ../src/imageview.c:578 msgid "200%" msgstr "" #: ../src/imageview.c:578 msgid "Zoom to 200%" msgstr "" #: ../src/imageview.c:580 msgid "400%" msgstr "" #: ../src/imageview.c:580 msgid "Zoom to 400%" msgstr "" #: ../src/imageview.c:582 msgid "800%" msgstr "" #: ../src/imageview.c:582 msgid "Zoom to 800%" msgstr "" #: ../src/imageview.c:584 msgid "1600%" msgstr "" #: ../src/imageview.c:584 msgid "Zoom to 1600%" msgstr "" #: ../src/iregion.c:166 #, c-format msgid "at (%d, %d), size (%d, %d)" msgstr "" #: ../src/iregion.c:181 ../src/iregion.c:223 msgid "Left" msgstr "" #: ../src/iregion.c:182 ../src/iregion.c:226 msgid "Top" msgstr "" #: ../src/iregion.c:183 ../src/iregion.c:229 msgid "Width" msgstr "" #: ../src/iregion.c:184 ../src/iregion.c:232 msgid "Height" msgstr "" #: ../src/iregion.c:223 msgid "Left edge of region" msgstr "" #: ../src/iregion.c:226 msgid "Top edge of region" msgstr "" #: ../src/iregion.c:229 msgid "Width of region" msgstr "" #: ../src/iregion.c:232 msgid "Height of region" msgstr "" #: ../src/iregion.c:237 #, c-format msgid "Edit \"%s\"" msgstr "" #: ../src/iregion.c:241 msgid "Set Region" msgstr "" #: ../src/itext.c:67 msgid "Formula" msgstr "" #: ../src/itext.c:184 ../src/itext.c:325 msgid "no value" msgstr "" #: ../src/itext.c:503 ../src/itext.c:536 msgid "Dirty value" msgstr "" #. Common menus. #. #: ../src/iwindow.c:659 msgid "_File" msgstr "" #: ../src/iwindow.c:660 msgid "_New" msgstr "" #: ../src/iwindow.c:661 ../src/mainw.c:1765 ../src/program.c:727 #: ../src/regionview.c:1016 ../src/rowview.c:586 msgid "_Edit" msgstr "" #: ../src/iwindow.c:662 msgid "_View" msgstr "" #: ../src/iwindow.c:663 msgid "_Help" msgstr "" #: ../src/iwindow.c:668 msgid "_Close" msgstr "" #: ../src/iwindow.c:669 msgid "Close" msgstr "" #: ../src/iwindow.c:673 msgid "_Quit" msgstr "" #: ../src/iwindow.c:674 msgid "Quit nip2" msgstr "" #: ../src/iwindow.c:677 msgid "_Contents" msgstr "" #: ../src/iwindow.c:678 msgid "Open the users guide" msgstr "" #: ../src/iwindow.c:682 msgid "_About" msgstr "" #: ../src/iwindow.c:683 msgid "About this program" msgstr "" #: ../src/iwindow.c:687 msgid "_Website" msgstr "" #: ../src/iwindow.c:688 msgid "Open the VIPS Homepage" msgstr "" #: lex.l:91 lex.l:101 msgid "line too long" msgstr "" #: lex.l:109 msgid "end of line inside string" msgstr "" #: lex.l:111 msgid "no end of string" msgstr "" #: lex.l:132 lex.l:142 lex.l:150 msgid "bad char constant" msgstr "" #: lex.l:175 msgid "nested comment" msgstr "" #: lex.l:181 msgid "no end of comment" msgstr "" #: lex.l:311 #, c-format msgid "bad number %s" msgstr "" #: lex.l:364 #, c-format msgid "illegal character \"%c\"" msgstr "" #: ../src/link.c:578 msgid "Circular dependency." msgstr "" #: ../src/link.c:579 #, c-format msgid "Circular dependency detected near symbol \"%s\"." msgstr "" #: ../src/main.c:116 msgid "evaluate and print EXPRESSION" msgstr "" #: ../src/main.c:119 msgid "load FILE as a set of definitions" msgstr "" #: ../src/main.c:122 msgid "write value of 'main' to FILE" msgstr "" #: ../src/main.c:124 msgid "run in batch mode" msgstr "" #: ../src/main.c:126 msgid "set values" msgstr "" #: ../src/main.c:128 msgid "verbose error output" msgstr "" #: ../src/main.c:131 msgid "don't load menu definitions" msgstr "" #: ../src/main.c:133 msgid "don't try to load command-line arguments" msgstr "" #: ../src/main.c:135 msgid "load stdin as a workspace" msgstr "" #: ../src/main.c:137 msgid "load stdin as a set of definitions" msgstr "" #: ../src/main.c:139 msgid "print value of 'main' to stdout" msgstr "" #: ../src/main.c:142 msgid "start up and shut down" msgstr "" #: ../src/main.c:145 msgid "time image save operations" msgstr "" #: ../src/main.c:148 msgid "start as if installed to PREFIX" msgstr "" #: ../src/main.c:150 msgid "output strings for internationalisation" msgstr "" #: ../src/main.c:153 msgid "print version number" msgstr "" #: ../src/main.c:156 msgid "test for errors and quit" msgstr "" #: ../src/main.c:235 #, c-format msgid "error calculating \"%s\"" msgstr "" #: ../src/main.c:243 #, c-format msgid "error saving \"%s\"" msgstr "" #: ../src/main.c:297 msgid "no \"main\" found" msgstr "" #: ../src/main.c:475 msgid "Unknown file type." msgstr "" #: ../src/main.c:476 #, c-format msgid "Unable to load \"%s\"." msgstr "" #: ../src/main.c:487 msgid "Unable to load." msgstr "" #: ../src/main.c:488 #, c-format msgid "Error loading plug-in \"%s\"." msgstr "" #: ../src/main.c:696 msgid "Next _Error" msgstr "" #: ../src/main.c:697 msgid "Ink dropper" msgstr "" #: ../src/main.c:698 msgid "D_uplicate" msgstr "" #: ../src/main.c:699 msgid "Pen" msgstr "" #: ../src/main.c:700 ../src/plot.c:94 msgid "Line" msgstr "" #: ../src/main.c:701 ../src/matrix.c:149 msgid "Text" msgstr "" #. IMAGEMODEL_TEXT #: ../src/main.c:702 ../src/paintboxview.c:259 msgid "Smudge" msgstr "" #: ../src/main.c:703 msgid "Flood" msgstr "" #: ../src/main.c:704 msgid "Flood Blob" msgstr "" #: ../src/main.c:705 msgid "Fill Rectangle" msgstr "" #: ../src/main.c:706 msgid "Pan" msgstr "" #: ../src/main.c:707 msgid "Select" msgstr "" #. And the LEDs we use. #. #: ../src/main.c:711 msgid "Red LED" msgstr "" #: ../src/main.c:712 msgid "Green LED" msgstr "" #: ../src/main.c:713 msgid "Blue LED" msgstr "" #: ../src/main.c:714 msgid "Yellow LED" msgstr "" #: ../src/main.c:715 msgid "Cyan LED" msgstr "" #: ../src/main.c:716 msgid "Off LED" msgstr "" #: ../src/main.c:895 msgid "Empty temp area" msgstr "" #: ../src/main.c:896 msgid "Many files in temp area." msgstr "" #: ../src/main.c:897 #, c-format msgid "" "The temp area \"%s\" contains %s of files. Would you like to empty the temp " "area? This will delete any workspace backups and cannot be undone." msgstr "" #: ../src/main.c:913 #, c-format msgid "unable to make %s %s: %s" msgstr "" #: ../src/main.c:1095 msgid "- image processing spreadsheet" msgstr "" #: ../src/main.c:1127 #, c-format msgid "linked to vips-%s" msgstr "" #. -1 means can't-be-set, at least on os x, so don't #. * warn. #. #: ../src/main.c:1187 #, c-format msgid "" "unable to change max file descriptors\n" "max file descriptors still set to %d" msgstr "" #: ../src/main.c:1193 msgid "unable to read max file descriptors" msgstr "" #: ../src/main.c:1417 ../src/main.c:1463 #, c-format msgid "" "Startup error log:\n" "%s" msgstr "" #: ../src/main.c:1462 msgid "Startup error." msgstr "" #: ../src/main.c:1473 #, c-format msgid "Welcome to %s-%s!" msgstr "" #: ../src/main.c:1478 #, c-format msgid "" "A new directory has been created to hold startup, data and temporary files:\n" "\n" " %s\n" "\n" "If you've used previous versions of %s, you might want to copy files over " "from your old work area." msgstr "" #: ../src/mainw.c:173 msgid "Compatibility mode." msgstr "" #: ../src/mainw.c:174 #, c-format msgid "" "This workspace was created by version %d.%d.%d. A set of compatibility menus " "have been loaded for this window." msgstr "" #: ../src/mainw.c:350 msgid "No temp area" msgstr "" #: ../src/mainw.c:356 #, c-format msgid "%s free" msgstr "" #: ../src/mainw.c:369 #, c-format msgid "%d cells free" msgstr "" #. Display select message instead. #. #: ../src/mainw.c:384 msgid "Selected:" msgstr "" #: ../src/mainw.c:414 msgid "compatibility mode" msgstr "" #: ../src/mainw.c:424 msgid "unsaved workspace" msgstr "" #. Expands to (eg.) "14GB free in /pics/tmp" #: ../src/mainw.c:607 #, c-format msgid " in \"%s\"" msgstr "" #: ../src/mainw.c:611 #, c-format msgid "%d cells in heap, %d cells free, %d cells maximum" msgstr "" #: ../src/mainw.c:615 #, c-format msgid "%d objects in workspace" msgstr "" #: ../src/mainw.c:619 #, c-format msgid "%d vips calls cached" msgstr "" #: ../src/mainw.c:623 #, c-format msgid "using %d threads" msgstr "" #: ../src/mainw.c:755 msgid "Find in workspace not implemented yet." msgstr "" #: ../src/mainw.c:763 msgid "Find again in workspace not implemented yet." msgstr "" #: ../src/mainw.c:798 msgid "No errors." msgstr "" #: ../src/mainw.c:799 msgid "There are no errors (that I can see) in this workspace." msgstr "" #: ../src/mainw.c:869 msgid "Completely recalculate?" msgstr "" #: ../src/mainw.c:1023 #, c-format msgid "" "Unable to execute:\n" " %s" msgstr "" #: ../src/mainw.c:1041 ../src/mainw.c:1074 msgid "Open File" msgstr "" #: ../src/mainw.c:1188 msgid "Recent Images" msgstr "" #: ../src/mainw.c:1196 msgid "Recent Workspaces" msgstr "" #: ../src/mainw.c:1204 msgid "Recent Matricies" msgstr "" #: ../src/mainw.c:1213 msgid "No recent items" msgstr "" #: ../src/mainw.c:1219 msgid "Clear Recent Menu" msgstr "" #: ../src/mainw.c:1279 msgid "Merge Workspace from File" msgstr "" #: ../src/mainw.c:1407 msgid "Set column name here" msgstr "" #: ../src/mainw.c:1409 msgid "Set column caption here" msgstr "" #: ../src/mainw.c:1411 msgid "New Column" msgstr "" #: ../src/mainw.c:1415 msgid "Create Column" msgstr "" #: ../src/mainw.c:1554 msgid "Revert to Defaults" msgstr "" #: ../src/mainw.c:1555 msgid "Revert to installation defaults?" msgstr "" #: ../src/mainw.c:1556 msgid "" "Would you like to reset all preferences to their factory settings? This will " "delete any changes you have ever made to your preferences and may take a few " "seconds." msgstr "" #: ../src/mainw.c:1573 msgid "Preferences" msgstr "" #: ../src/mainw.c:1578 msgid "Revert to Defaults ..." msgstr "" #. Menu items. #. #: ../src/mainw.c:1641 msgid "Open _Recent" msgstr "" #: ../src/mainw.c:1642 ../src/mainw.c:2107 msgid "Jump to _Column" msgstr "" #: ../src/mainw.c:1643 msgid "_Toolkits" msgstr "" #: ../src/mainw.c:1653 ../src/mainw.c:2104 msgid "New C_olumn" msgstr "" #: ../src/mainw.c:1654 msgid "Create a new column" msgstr "" #: ../src/mainw.c:1658 msgid "New Named C_olumn" msgstr "" #: ../src/mainw.c:1659 msgid "Create a new column with a specified name" msgstr "" #: ../src/mainw.c:1663 ../src/program.c:1669 msgid "New _Workspace" msgstr "" #: ../src/mainw.c:1664 msgid "Create a new workspace" msgstr "" #: ../src/mainw.c:1668 msgid "_Open" msgstr "" #: ../src/mainw.c:1669 msgid "Open a file" msgstr "" #: ../src/mainw.c:1673 msgid "Open _Examples" msgstr "" #: ../src/mainw.c:1674 msgid "Open example workspaces" msgstr "" #: ../src/mainw.c:1678 msgid "_Duplicate Workspace" msgstr "" #: ../src/mainw.c:1679 msgid "Duplicate workspace" msgstr "" #: ../src/mainw.c:1683 msgid "_Merge Workspace" msgstr "" #: ../src/mainw.c:1684 msgid "Merge workspace into this workspace" msgstr "" #: ../src/mainw.c:1688 msgid "_Save Workspace" msgstr "" #: ../src/mainw.c:1689 msgid "Save workspace" msgstr "" #: ../src/mainw.c:1693 msgid "_Save Workspace As" msgstr "" #: ../src/mainw.c:1694 msgid "Save workspace as" msgstr "" #: ../src/mainw.c:1698 msgid "Search for Workspace _Backups" msgstr "" #: ../src/mainw.c:1699 msgid "Load last automatically backed-up workspace" msgstr "" #: ../src/mainw.c:1703 ../src/program.c:1714 msgid "_Delete" msgstr "" #: ../src/mainw.c:1704 msgid "Delete selected items" msgstr "" #: ../src/mainw.c:1709 msgid "Select all items" msgstr "" #: ../src/mainw.c:1713 msgid "D_uplicate Selected" msgstr "" #: ../src/mainw.c:1714 msgid "Duplicate selected items" msgstr "" #: ../src/mainw.c:1718 ../src/rowview.c:596 msgid "_Recalculate" msgstr "" #: ../src/mainw.c:1719 msgid "Recalculate selected items" msgstr "" #: ../src/mainw.c:1723 ../src/mainw.c:2108 msgid "Align _Columns" msgstr "" #: ../src/mainw.c:1724 msgid "Align columns to grid" msgstr "" #: ../src/mainw.c:1728 ../src/program.c:1739 msgid "_Find" msgstr "" #: ../src/mainw.c:1729 msgid "Find in workspace" msgstr "" #: ../src/mainw.c:1733 ../src/program.c:1744 msgid "Find _Next" msgstr "" #: ../src/mainw.c:1734 msgid "Find again in workspace" msgstr "" #: ../src/mainw.c:1739 msgid "Jump to next error" msgstr "" #: ../src/mainw.c:1743 msgid "_Group" msgstr "" #: ../src/mainw.c:1744 msgid "Group selected items" msgstr "" #: ../src/mainw.c:1748 ../src/rowview.c:590 msgid "U_ngroup" msgstr "" #: ../src/mainw.c:1749 msgid "Ungroup selected items" msgstr "" #: ../src/mainw.c:1753 msgid "_Preferences" msgstr "" #: ../src/mainw.c:1754 msgid "Edit preferences" msgstr "" #: ../src/mainw.c:1759 msgid "Workspace as Grap_h" msgstr "" #: ../src/mainw.c:1760 msgid "Show a graph of workspace dependencies" msgstr "" #: ../src/mainw.c:1766 msgid "Edit toolkits" msgstr "" #: ../src/mainw.c:1772 msgid "Au_to Recalculate" msgstr "" #: ../src/mainw.c:1773 msgid "Recalculate automatically on change" msgstr "" #: ../src/mainw.c:1777 msgid "_Toolbar" msgstr "" #: ../src/mainw.c:1778 msgid "Show window toolbar" msgstr "" #: ../src/mainw.c:1782 msgid "_Statusbar" msgstr "" #: ../src/mainw.c:1783 msgid "Show window statusbar" msgstr "" #: ../src/mainw.c:1787 msgid "Toolkit _Browser" msgstr "" #: ../src/mainw.c:1788 msgid "Show toolkit browser" msgstr "" #: ../src/mainw.c:1792 msgid "Workspace _Definitions" msgstr "" #: ../src/mainw.c:1793 msgid "Show workspace definitions" msgstr "" #: ../src/mainw.c:1799 msgid "_Normal" msgstr "" #: ../src/mainw.c:1800 msgid "Normal view mode" msgstr "" #: ../src/mainw.c:1804 msgid "Show _Formula" msgstr "" #: ../src/mainw.c:1805 msgid "Show formula view mode" msgstr "" #: ../src/mainw.c:1809 msgid "No _Edits" msgstr "" #: ../src/mainw.c:1810 msgid "No edits view mode" msgstr "" #: ../src/mainw.c:2103 msgid "Workspace menu" msgstr "" #: ../src/mainw.c:2113 msgid "_Merge Workspace from File" msgstr "" #: ../src/mainw.c:2116 msgid "_Group Selected" msgstr "" #. Toolkit Browser pane. #. #: ../src/mainw.c:2125 msgid "Toolkit Browser" msgstr "" #. Workspace-local defs pane. #. #: ../src/mainw.c:2144 msgid "Workspace Definitions" msgstr "" #: ../src/matrix.c:150 msgid "Sliders" msgstr "" #: ../src/matrix.c:151 msgid "Toggle buttons" msgstr "" #: ../src/matrix.c:152 msgid "Text, plus scale and offset" msgstr "" #: ../src/matrix.c:163 msgid "Display as" msgstr "" #: ../src/matrix.c:194 #, c-format msgid "Edit Matrix \"%s\"" msgstr "" #: ../src/matrix.c:199 msgid "Set Matrix" msgstr "" #: ../src/matrix.c:265 ../src/matrixview.c:425 msgid "Scale" msgstr "" #: ../src/matrix.c:268 ../src/matrixview.c:430 msgid "Offset" msgstr "" #: ../src/matrix.c:274 msgid "Display" msgstr "" #: ../src/matrixview.c:190 ../src/matrixview.c:211 #, c-format msgid "" "Cell (%d, %d):\n" "%s" msgstr "" #. Expands to (eg) "A2: cell (1,2): 45" ... status line display during #. * matrix traverse. #. #: ../src/matrixview.c:622 #, c-format msgid ": cell (%d, %d): %s" msgstr "" #: ../src/model.c:131 #, c-format msgid "" "Unable to load from file \"%s\". Error log is:\n" "%s" msgstr "" #: ../src/model.c:420 msgid "model_save: xmlNewChild() failed" msgstr "" #: ../src/model.c:487 msgid "XML load error." msgstr "" #: ../src/model.c:488 #, c-format msgid "Can't load node of type \"%s\" into object of type \"%s\"" msgstr "" #: ../src/model.c:716 msgid "Delete?" msgstr "" #: ../src/model.c:717 #, c-format msgid "Are you sure you want to delete %s \"%s\"?" msgstr "" #: ../src/option.c:68 msgid "Labels" msgstr "" #. Create signals. #. #. Init methods. #. #: ../src/paintboxview.c:109 msgid "Paintbox bar menu" msgstr "" #: ../src/paintboxview.c:210 msgid "Clear undo history?" msgstr "" #: ../src/paintboxview.c:211 msgid "" "Are you sure you want to clear all undo and redo? This will free up memory, " "but you will no longer be able to undo or redo any of the painting you have " "done so far." msgstr "" #: ../src/paintboxview.c:246 msgid "Manipulate regions" msgstr "" #. IMAGEMODEL_SELECT #: ../src/paintboxview.c:247 msgid "Pan window" msgstr "" #. IMAGEMODEL_PAN #: ../src/paintboxview.c:248 msgid "Zoom in on mouse" msgstr "" #. IMAGEMODEL_MAGOUT #: ../src/paintboxview.c:250 msgid "Read pixel into inkwell" msgstr "" #. IMAGEMODEL_DROPPER #: ../src/paintboxview.c:251 msgid "Freehand draw " msgstr "" #. IMAGEMODEL_PEN #: ../src/paintboxview.c:252 msgid "Draw straight lines" msgstr "" #. IMAGEMODEL_LINE #: ../src/paintboxview.c:253 msgid "Fill rectangles" msgstr "" #. IMAGEMODEL_RECT #: ../src/paintboxview.c:254 msgid "Flood while pixel not equal to ink" msgstr "" #. IMAGEMODEL_FLOOD #: ../src/paintboxview.c:256 msgid "Flood while pixel equal to click" msgstr "" #. IMAGEMODEL_BLOB #: ../src/paintboxview.c:258 msgid "Draw text" msgstr "" #: ../src/paintboxview.c:309 msgid "Undo last paint action" msgstr "" #: ../src/paintboxview.c:318 msgid "Redo last paint action" msgstr "" #: ../src/paintboxview.c:327 msgid "Clear all undo and redo buffers" msgstr "" #: ../src/paintboxview.c:376 msgid "Enter text for text tool" msgstr "" #: ../src/panechild.c:108 msgid "Close the pane" msgstr "" #: parse.y:244 parse.y:255 msgid "not top level" msgstr "" #: parse.y:260 msgid "not strings" msgstr "" #: parse.y:318 msgid "left-hand-side pattern contains no identifiers" msgstr "" #: parse.y:1064 ../src/program.c:1421 msgid "Parse error." msgstr "" #: parse.y:1067 #, c-format msgid "Error in %s: %s" msgstr "" #: parse.y:1070 #, c-format msgid "Error: %s" msgstr "" #: parse.y:1183 msgid "definition is too long" msgstr "" #: parse.y:1607 msgid "no leading identifier" msgstr "" #: parse.y:1613 msgid "'=' missing" msgstr "" #: parse.y:1643 msgid "identifier expected" msgstr "" #: parse.y:1653 #, c-format msgid "'%s' does not exist" msgstr "" #: parse.y:1655 #, c-format msgid "'%s' has no members" msgstr "" #: parse.y:1670 msgid "'.' or '=' expected" msgstr "" #. Not found? Maybe - error message anyway. #. #: ../src/path.c:332 ../src/path.c:360 ../src/program.c:1440 #: ../src/program.c:1492 ../src/program.c:1514 ../src/program.c:1522 #: ../src/symbol.c:502 msgid "Not found." msgstr "" #: ../src/path.c:333 #, c-format msgid "File \"%s\" not found." msgstr "" #: ../src/path.c:361 #, c-format msgid "File \"%s\" not found on path" msgstr "" #: ../src/pathnameview.c:173 msgid "Select a new file name" msgstr "" #: ../src/plot.c:80 msgid "YYYY" msgstr "" #: ../src/plot.c:81 msgid "XYYY" msgstr "" #: ../src/plot.c:82 msgid "XYXY" msgstr "" #: ../src/plot.c:93 msgid "Point" msgstr "" #: ../src/plot.c:95 msgid "Spline" msgstr "" #: ../src/plot.c:96 msgid "Bar" msgstr "" #: ../src/plot.c:141 msgid "More than one column needed or XY plots" msgstr "" #: ../src/plot.c:151 msgid "Even number of columns only for XY format plots" msgstr "" #: ../src/plot.c:316 msgid "Format" msgstr "" #: ../src/plot.c:319 msgid "Style" msgstr "" #: ../src/plot.c:322 msgid "Xmin" msgstr "" #: ../src/plot.c:325 msgid "Xmax" msgstr "" #: ../src/plot.c:328 msgid "Ymin" msgstr "" #: ../src/plot.c:331 msgid "Ymax" msgstr "" #: ../src/plot.c:337 msgid "Options" msgstr "" #: ../src/plot.c:361 msgid "1xn or nx1 images only for Plot" msgstr "" #: ../src/plot.c:380 msgid "Unable to prepare image." msgstr "" #: ../src/plot.c:393 msgid "1xn or nx1 images only" msgstr "" #. Create signals. #. #. Init methods. #. #: ../src/plotstatus.c:99 ../src/statusview.c:108 msgid "Status bar menu" msgstr "" #: ../src/plotstatus.c:190 ../src/statusview.c:223 ../src/statusview.c:226 msgid "Magnification" msgstr "" #. Probably failed to load prefs on startup for some reason. #. #: ../src/prefs.c:207 msgid "Unable to display preferences." msgstr "" #: ../src/prefs.c:208 #, c-format msgid "" "No preferences workspace was found. Preferences probably failed to load when " "%s started." msgstr "" #: ../src/program.c:73 msgid "Edit window" msgstr "" #: ../src/program.c:263 ../src/workspacedefs.c:108 msgid "modified" msgstr "" #: ../src/program.c:625 msgid "Menu item text" msgstr "" #: ../src/program.c:628 msgid "Load column from this file" msgstr "" #: ../src/program.c:630 #, c-format msgid "Edit Column Item \"%s\"" msgstr "" #: ../src/program.c:635 msgid "Set column item" msgstr "" #: ../src/program.c:656 ../src/program.c:662 msgid "Unable to save." msgstr "" #: ../src/program.c:657 msgid "You can only save toolkits, not tools." msgstr "" #: ../src/program.c:663 msgid "You can't save auto-generated toolkits." msgstr "" #. Create signals. #. #. Init methods. #. #: ../src/program.c:726 msgid "Toolkit menu" msgstr "" #: ../src/program.c:1093 msgid "Set toolkit name here" msgstr "" #: ../src/program.c:1095 msgid "Set toolkit caption here" msgstr "" #: ../src/program.c:1096 msgid "New Toolkit" msgstr "" #: ../src/program.c:1100 ../src/program.c:1178 msgid "Create" msgstr "" #: ../src/program.c:1111 msgid "Nothing selected." msgstr "" #: ../src/program.c:1112 msgid "No toolkit selected." msgstr "" #: ../src/program.c:1171 msgid "Display this name" msgstr "" #: ../src/program.c:1173 msgid "Load this file" msgstr "" #: ../src/program.c:1241 msgid "Load Definition" msgstr "" #: ../src/program.c:1295 msgid "Reload" msgstr "" #: ../src/program.c:1296 msgid "Reload startup objects?" msgstr "" #: ../src/program.c:1297 msgid "" "Would you like to reload all startup menus, workspaces and plugins now? This " "may take a few seconds." msgstr "" #: ../src/program.c:1379 msgid "No tool selected" msgstr "" #: ../src/program.c:1422 msgid "Bad regular expression." msgstr "" #: ../src/program.c:1441 #, c-format msgid "No match found for \"%s\"." msgstr "" #: ../src/program.c:1454 msgid "Find in all Toolkits" msgstr "" #: ../src/program.c:1464 msgid "Enter search string here" msgstr "" #: ../src/program.c:1515 #, c-format msgid "No top-level symbol called \"%s\"." msgstr "" #: ../src/program.c:1523 #, c-format msgid "Symbol \"%s\" has no tool inforation." msgstr "" #: ../src/program.c:1544 msgid "Go to definition of this symbol" msgstr "" #: ../src/program.c:1546 msgid "Go to Definition" msgstr "" #: ../src/program.c:1576 msgid "Object information." msgstr "" #: ../src/program.c:1627 msgid "No documentation available." msgstr "" #: ../src/program.c:1628 msgid "" "On-line documentation is only currently available for VIPS functions and nip " "builtins." msgstr "" #: ../src/program.c:1644 msgid "New _Tool" msgstr "" #: ../src/program.c:1645 msgid "Make a new tool" msgstr "" #: ../src/program.c:1649 msgid "New Tool_kit" msgstr "" #: ../src/program.c:1650 msgid "Make a new toolkit" msgstr "" #: ../src/program.c:1654 msgid "New _Separator" msgstr "" #: ../src/program.c:1655 msgid "Make a new separator" msgstr "" #: ../src/program.c:1659 msgid "New _Column Item" msgstr "" #: ../src/program.c:1660 msgid "Make a new column item" msgstr "" #: ../src/program.c:1664 msgid "New _Program Window" msgstr "" #: ../src/program.c:1665 msgid "Make a new program window" msgstr "" #: ../src/program.c:1670 msgid "Make a new workspace" msgstr "" #: ../src/program.c:1674 msgid "_Open Toolkit" msgstr "" #: ../src/program.c:1675 msgid "_Open toolkit" msgstr "" #: ../src/program.c:1679 msgid "Save Toolkit" msgstr "" #: ../src/program.c:1680 msgid "_Save toolkit" msgstr "" #: ../src/program.c:1684 msgid "Save Toolkit _As" msgstr "" #: ../src/program.c:1685 msgid "Save toolkit as" msgstr "" #: ../src/program.c:1689 msgid "_Process" msgstr "" #: ../src/program.c:1690 msgid "Process text" msgstr "" #: ../src/program.c:1694 msgid "_Reload All Toolkits" msgstr "" #: ../src/program.c:1695 msgid "Remove and reload all startup data" msgstr "" #: ../src/program.c:1699 msgid "C_ut" msgstr "" #: ../src/program.c:1700 msgid "Cut selected text" msgstr "" #: ../src/program.c:1704 msgid "_Copy" msgstr "" #: ../src/program.c:1705 msgid "Copy selected text" msgstr "" #: ../src/program.c:1709 msgid "_Paste" msgstr "" #: ../src/program.c:1710 msgid "Paste selected text" msgstr "" #: ../src/program.c:1715 msgid "Delete selected text" msgstr "" #: ../src/program.c:1720 msgid "Select all text" msgstr "" #: ../src/program.c:1724 msgid "Dese_lect All" msgstr "" #: ../src/program.c:1725 msgid "Deselect all text" msgstr "" #: ../src/program.c:1729 msgid "Delete _Tool" msgstr "" #: ../src/program.c:1730 msgid "Delete current tool" msgstr "" #: ../src/program.c:1734 msgid "Delete Tool_kit" msgstr "" #: ../src/program.c:1735 msgid "Delete current toolkit" msgstr "" #: ../src/program.c:1740 msgid "Find text in toolkits" msgstr "" #: ../src/program.c:1745 msgid "Find text again" msgstr "" #: ../src/program.c:1749 msgid "_Jump To Definition" msgstr "" #: ../src/program.c:1750 msgid "Jump to definition" msgstr "" #: ../src/program.c:1754 msgid "_Info" msgstr "" #: ../src/program.c:1755 msgid "Info on selected object" msgstr "" #: ../src/program.c:1759 msgid "_Trace" msgstr "" #: ../src/program.c:1760 msgid "Make a new trace window" msgstr "" #: ../src/program.c:1764 msgid "_Errors" msgstr "" #: ../src/program.c:1765 msgid "Show all errors" msgstr "" #: ../src/program.c:1769 msgid "Help on _Tool" msgstr "" #: ../src/program.c:1770 msgid "View docs for this tool" msgstr "" #: ../src/program.c:2015 msgid "Bad drag." msgstr "" #: ../src/program.c:2017 msgid "" "Sorry, you can only drag tools between toolkits. You can't reorder toolkits, " "you can't nest toolkits and you can't drag tools to the top level." msgstr "" #: ../src/program.c:2196 msgid "Tool" msgstr "" #: ../src/progress.c:120 msgid "Cancelling" msgstr "" #: ../src/progress.c:153 #, c-format msgid "%d minute left" msgid_plural "%d minutes left" msgstr[0] "" msgstr[1] "" #: ../src/progress.c:158 #, c-format msgid "%d second left" msgid_plural "%d seconds left" msgstr[0] "" msgstr[1] "" #: ../src/progress.c:179 msgid "Calculating" msgstr "" #: ../src/progress.c:198 msgid "Loading" msgstr "" #: ../src/reduce.c:135 msgid "Typecheck error." msgstr "" #: ../src/reduce.c:137 #, c-format msgid "%s expected %s, instead saw:" msgstr "" #: ../src/reduce.c:148 ../src/reduce.c:1056 ../src/trace.c:126 #: ../src/workspace.c:1704 msgid "Overflow error." msgstr "" #: ../src/reduce.c:149 #, c-format msgid "%s too long." msgstr "" #: ../src/reduce.c:532 ../src/reduce.c:597 msgid "Not rectangular." msgstr "" #: ../src/reduce.c:533 ../src/reduce.c:598 #, c-format msgid "" "Matrix of real is not rectangular. Found row of length %d, should be %d." msgstr "" #: ../src/reduce.c:557 msgid "Zero dimension." msgstr "" #: ../src/reduce.c:558 #, c-format msgid "Matrix has width %d, height %d." msgstr "" #: ../src/reduce.c:850 msgid "List length" msgstr "" #: ../src/reduce.c:899 #, c-format msgid "List index must be positive, not %d" msgstr "" #: ../src/reduce.c:907 msgid "List index" msgstr "" #: ../src/reduce.c:912 #, c-format msgid "List only has %d elements, unable to get element %d." msgstr "" #: ../src/reduce.c:955 msgid "No arguments allowed." msgstr "" #: ../src/reduce.c:956 #, c-format msgid "Object \"%s\" should have no arguments." msgstr "" #: ../src/reduce.c:1057 msgid "C stack overflow. Expression too complex." msgstr "" #: ../src/reduce.c:1073 msgid "Cancelled." msgstr "" #: ../src/reduce.c:1074 msgid "Evaluation cancelled." msgstr "" #: ../src/reduce.c:1169 msgid "No value." msgstr "" #: ../src/reduce.c:1170 #, c-format msgid "Symbol \"%s\" has no value." msgstr "" #: ../src/reduce.c:1645 ../src/reduce.c:1650 ../src/reduce.c:1656 msgid "List generator" msgstr "" #: ../src/reduce.h:75 ../src/reduce.h:105 msgid "Stack overflow." msgstr "" #: ../src/reduce.h:76 msgid "Spine stack overflow, runaway recursion?" msgstr "" #: ../src/reduce.h:106 msgid "Frame stack overflow, expression too complex." msgstr "" #: ../src/reduce.h:119 ../src/reduce.h:131 msgid "Stack underflow." msgstr "" #: ../src/reduce.h:120 msgid "Frame stack underflow, you've found a bug!" msgstr "" #: ../src/reduce.h:132 msgid "Spine stack underflow, you've found a bug!" msgstr "" #: ../src/regionview.c:935 ../src/rowview.c:302 msgid "Can't duplicate." msgstr "" #: ../src/regionview.c:937 msgid "You can only duplicate top level regions." msgstr "" #: ../src/regionview.c:979 msgid "Can't delete." msgstr "" #: ../src/regionview.c:980 msgid "You can only delete top level regions." msgstr "" #: ../src/regionview.c:989 msgid "Delete Region?" msgstr "" #: ../src/regionview.c:990 #, c-format msgid "Are you sure you want to delete Region \"%s\"?" msgstr "" #. Other init. #. #: ../src/regionview.c:1015 msgid "Region menu" msgstr "" #: ../src/row.c:288 msgid "Error in row." msgstr "" #. Elements are name of row, principal error, #. * secondary error. #. #: ../src/row.c:292 #, c-format msgid "" "Error in row %s: %s\n" "%s" msgstr "" #. Expands to eg. "B1 refers to: B2, B3". #. #: ../src/row.c:497 msgid "refers to" msgstr "" #. Expands to eg. "B1 is referred to by: B2, B3". #. #: ../src/row.c:508 msgid "is referred to by" msgstr "" #: ../src/row.c:521 msgid "is blocked on" msgstr "" #: ../src/rowview.c:304 msgid "You can only duplicate top level rows." msgstr "" #: ../src/rowview.c:457 msgid "Drag between columns not yet implemented." msgstr "" #. Other init. #. #: ../src/rowview.c:585 msgid "Row menu" msgstr "" #: ../src/rowview.c:594 ../src/workspacedefs.c:262 msgid "Replace From _File" msgstr "" #: ../src/rowview.c:598 msgid "Re_set" msgstr "" #: ../src/rowview.c:664 msgid "Click to open or close class" msgstr "" #: ../src/slider.c:51 msgid "From" msgstr "" #: ../src/slider.c:54 msgid "To" msgstr "" #: ../src/spin.c:223 msgid "Expand or collapse row" msgstr "" #: ../src/symbol.c:503 #, c-format msgid "Symbol %s is not defined." msgstr "" #: ../src/symbol.c:507 #, c-format msgid "%s is referred to by" msgstr "" #: ../src/symbol.c:721 #, c-format msgid "Redefinition of \"%s\"." msgstr "" #: ../src/symbol.c:725 #, c-format msgid "Previously defined at line %d." msgstr "" #: ../src/symbol.c:748 #, c-format msgid "Attempt to redefine root symbol \"%s\"." msgstr "" #. Parameter, workspace, etc. #. #: ../src/symbol.c:773 #, c-format msgid "Can't redefine %s \"%s\"." msgstr "" #. used as in "fred refers to undefined symbol jim" #. #: ../src/tool.c:75 msgid "refers to undefined symbol" msgstr "" #: ../src/tool.c:868 #, c-format msgid "" "Can't create dialog with name \"%s\", an object with that name already " "exists in kit \"%s\"." msgstr "" #: ../src/toolkitbrowser.c:242 msgid "Search:" msgstr "" #: ../src/toolkitbrowser.c:267 msgid "Action" msgstr "" #: ../src/toolkitbrowser.c:275 msgid "Parameters" msgstr "" #: ../src/toolkitbrowser.c:283 msgid "Menu Item" msgstr "" #: ../src/toolkitgroup.c:139 #, c-format msgid "Toolkits for %s" msgstr "" #: ../src/trace.c:127 msgid "Trace buffer stack overflow." msgstr "" #: ../src/trace.c:231 msgid "Clear trace window" msgstr "" #: ../src/trace.c:237 msgid "_Operators" msgstr "" #: ../src/trace.c:238 msgid "trace operators" msgstr "" #: ../src/trace.c:242 msgid "_Builtin Functions" msgstr "" #: ../src/trace.c:243 msgid "trace calls to built in functions" msgstr "" #: ../src/trace.c:247 msgid "_Class Construction" msgstr "" #: ../src/trace.c:248 msgid "trace class constructors" msgstr "" #: ../src/trace.c:252 msgid "_VIPS Operations" msgstr "" #: ../src/trace.c:253 msgid "trace calls to VIPS" msgstr "" #: ../src/trace.c:331 msgid "Trace" msgstr "" #: ../src/tslider.c:369 msgid "Slider value ... edit!" msgstr "" #: ../src/tslider.c:390 msgid "Left-drag to set number" msgstr "" #: ../src/util.c:211 msgid "Unable to set XML property." msgstr "" #: ../src/util.c:212 #, c-format msgid "Unable to set property \"%s\" to value \"%s\"." msgstr "" #: ../src/util.c:830 msgid "(no image)" msgstr "" #: ../src/util.c:844 #, c-format msgid "%dx%d %s, %d band, %s" msgid_plural "%dx%d %s, %d bands, %s" msgstr[0] "" msgstr[1] "" #: ../src/util.c:1257 msgid "8-bit unsigned integer" msgstr "" #. IM_BANDFMT_UCHAR #: ../src/util.c:1258 msgid "8-bit signed integer" msgstr "" #. IM_BANDFMT_CHAR #: ../src/util.c:1259 msgid "16-bit unsigned integer" msgstr "" #. IM_BANDFMT_USHORT #: ../src/util.c:1260 msgid "16-bit signed integer" msgstr "" #. IM_BANDFMT_SHORT #: ../src/util.c:1261 msgid "32-bit unsigned integer" msgstr "" #. IM_BANDFMT_UINT #: ../src/util.c:1262 msgid "32-bit signed integer" msgstr "" #. IM_BANDFMT_INT #: ../src/util.c:1263 msgid "32-bit float" msgstr "" #. IM_BANDFMT_FLOAT #: ../src/util.c:1264 msgid "64-bit complex" msgstr "" #. IM_BANDFMT_COMPLEX #: ../src/util.c:1265 msgid "64-bit float" msgstr "" #. IM_BANDFMT_DOUBLE #: ../src/util.c:1266 msgid "128-bit complex" msgstr "" #: ../src/util.c:1274 msgid "" msgstr "" #: ../src/util.c:1316 msgid "" msgstr "" #: ../src/util.c:1340 msgid "directory" msgstr "" #: ../src/util.c:1599 msgid "Unable to find install area." msgstr "" #: ../src/util.c:1775 msgid "" msgstr "" #: ../src/util.c:1933 msgid "Filename is too long." msgstr "" #: ../src/util.c:1939 msgid "Filename contains only blank characters." msgstr "" #: ../src/util.c:2002 ../src/util.c:2011 ../src/util.c:2066 msgid "Unable to open." msgstr "" #: ../src/util.c:2003 ../src/util.c:2012 #, c-format msgid "" "Unable to open file \"%s\" for reading.\n" "%s." msgstr "" #: ../src/util.c:2067 #, c-format msgid "" "Unable to open file \"%s\" for writing.\n" "%s." msgstr "" #: ../src/util.c:2088 msgid "Unable to write." msgstr "" #: ../src/util.c:2089 #, c-format msgid "" "Unable to write to file \"%s\".\n" "%s." msgstr "" #: ../src/util.c:2124 ../src/util.c:2137 ../src/util.c:2162 msgid "Unable to read." msgstr "" #: ../src/util.c:2125 #, c-format msgid "File \"%s\" too large." msgstr "" #: ../src/util.c:2138 ../src/util.c:2163 #, c-format msgid "" "Unable to read from file \"%s\".\n" "%s." msgstr "" #. File length unit. #. #: ../src/util.c:2454 msgid "bytes" msgstr "" #. Kilo byte unit. #. #: ../src/util.c:2458 msgid "KB" msgstr "" #. Mega byte unit. #. #: ../src/util.c:2462 msgid "MB" msgstr "" #. Giga byte unit. #. #: ../src/util.c:2466 msgid "GB" msgstr "" #. Tera byte unit. #. #: ../src/util.c:2470 msgid "TB" msgstr "" #: ../src/util.c:2513 msgid "Unable to create temporary file." msgstr "" #: ../src/util.c:2514 #, c-format msgid "" "Unable to make file \"%s\"\n" "%s" msgstr "" #: ../src/util.c:2721 msgid "Out of memory." msgstr "" #: ../src/util.c:2722 #, c-format msgid "Request for %s of RAM triggered memory allocation failure." msgstr "" #: ../src/vipsobject.c:94 msgid "No such type." msgstr "" #: ../src/vipsobject.c:95 #, c-format msgid "Type \"%s\" not found as a subclass of VipsObject." msgstr "" #: ../src/vipsobject.c:126 #, c-format msgid "No more than %d arguments allowed." msgstr "" #: ../src/vipsobject.c:253 msgid "Wrong number of required arguments." msgstr "" #: ../src/vipsobject.c:254 #, c-format msgid "Operation \"%s\" has %d required arguments, you supplied %d." msgstr "" #: ../src/vipsobject.c:285 msgid "Unable to build object." msgstr "" #: ../src/workspace.c:156 msgid "No objects selected." msgstr "" #: ../src/workspace.c:157 ../src/workspace.c:162 msgid "Select exactly one object and try again." msgstr "" #: ../src/workspace.c:161 msgid "More than one object selected." msgstr "" #: ../src/workspace.c:791 ../src/workspace.c:799 msgid "No backup workspaces found." msgstr "" #: ../src/workspace.c:793 msgid "" "You need to enable \"Auto workspace save\" in Preferences before automatic " "recovery works." msgstr "" #: ../src/workspace.c:800 #, c-format msgid "No suitable workspace save files found in \"%s\"" msgstr "" #: ../src/workspace.c:816 msgid "Open workspace backup?" msgstr "" #: ../src/workspace.c:817 #, c-format msgid "" "Found workspace \"%s\", dated %s. Do you want to recover this workspace?" msgstr "" #: ../src/workspace.c:1327 msgid "Version mismatch." msgstr "" #: ../src/workspace.c:1328 #, c-format msgid "" "File \"%s\" was saved from %s-%d.%d.%d. You may see compatibility problems." msgstr "" #: ../src/workspace.c:1485 msgid "// private definitions for this workspace\n" msgstr "" #: ../src/workspace.c:1530 #, c-format msgid "Can't create workspace \"%s\". A symbol with that name already exists." msgstr "" #: ../src/workspace.c:1621 msgid "Default empty workspace" msgstr "" #: ../src/workspace.c:1694 #, c-format msgid "%s needs %d arguments, there are %d selected." msgstr "" #: ../src/workspace.c:1705 msgid "Too many names selected." msgstr "" #: ../src/workspace.c:1845 msgid "You can only remove top level rows." msgstr "" #: ../src/workspace.c:1846 msgid "Not all selected objects are top level rows." msgstr "" #: ../src/workspace.c:1896 msgid "Delete selected objects?" msgstr "" #: ../src/workspace.c:1897 #, c-format msgid "Are you sure you want to delete %s?" msgstr "" #: ../src/workspace.c:1955 msgid "Unable to ungroup." msgstr "" #: ../src/workspace.c:1956 #, c-format msgid "Row \"%s\" is not a Group or a list." msgstr "" #: ../src/workspacedefs.c:97 #, c-format msgid "%d definition" msgid_plural "%d definitions" msgstr[0] "" msgstr[1] "" #: ../src/workspacedefs.c:103 msgid "errors" msgstr "" #: ../src/workspacedefs.c:181 msgid "Replace Definition From File" msgstr "" #: ../src/workspacedefs.c:261 msgid "Workspace definitions" msgstr "" #: ../src/workspacedefs.c:282 msgid "Process" msgstr "" #: ../src/workspacegroup.c:146 #, c-format msgid "" "Can't create workspacegroup \"%s\". A symbol with that name already exists." msgstr "" #: ../src/workspacegroup.c:213 msgid "Set workspace name here" msgstr "" #: ../src/workspacegroup.c:215 msgid "Set workspace caption here" msgstr "" #: ../src/workspacegroup.c:217 msgid "New Workspace" msgstr "" #: ../src/workspacegroup.c:221 msgid "Create Workspace" msgstr "" ================================================ FILE: po/malkovich.po ================================================ # test translation file # msgid "" msgstr "" "Project-Id-Version: nip2 7.11.11\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-06-09 21:25+0100\n" "PO-Revision-Date: 2006-09-06 12:30+0000\n" "Last-Translator: john \n" "Language-Team: dk \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #. Create signals. #. #. Init methods. #. #: src/plotstatus.c:99 src/statusview.c:108 msgid "Status bar menu" msgstr "Malkovich" #: src/plotstatus.c:190 src/statusview.c:223 src/statusview.c:226 msgid "Magnification" msgstr "Malkovich" #: src/graph.c:186 msgid "circular" msgstr "Malkovich" #: src/graph.c:191 #, fuzzy, c-format msgid "circular to label %d" msgstr "Malkovich" #: src/graph.c:201 #, fuzzy, c-format msgid "label %d" msgstr "Malkovich" #: src/graph.c:218 msgid "unevaluated" msgstr "Malkovich" #: src/graph.c:253 #, fuzzy, c-format msgid "class (%p)" msgstr "Malkovich" #: src/graph.c:264 msgid "members" msgstr "Malkovich" #: src/graph.c:275 msgid "secret" msgstr "Malkovich" #: src/graph.c:328 #, fuzzy, c-format msgid "no value (type %d)" msgstr "Malkovich" #: src/graph.c:336 msgid "NULL pointer" msgstr "Malkovich" #: src/graph.c:345 msgid "symbol" msgstr "Malkovich" #: src/graph.c:353 msgid "constructor" msgstr "Malkovich" #: src/graph.c:361 msgid "symref" msgstr "Malkovich" #: src/graph.c:369 msgid "compileref" msgstr "Malkovich" #: src/graph.c:397 #, fuzzy, c-format msgid "tag \"%s\"" msgstr "Malkovich" #: src/graph.c:412 #, fuzzy, c-format msgid "unknown element tag %d" msgstr "Malkovich" #: src/pathnameview.c:80 src/gtkutil.c:756 src/gtkutil.c:791 src/gtkutil.c:845 #: src/editview.c:79 src/formula.c:198 src/fontnameview.c:80 #: src/optionview.c:172 src/sliderview.c:74 #, fuzzy, c-format msgid "%s:" msgstr "Malkovich" #: src/pathnameview.c:173 msgid "Select a new file name" msgstr "Malkovich" #: src/pathname.c:81 src/slider.c:48 src/option.c:65 src/string.c:67 #: src/toggle.c:48 src/number.c:65 src/fontname.c:58 src/mainw.c:1335 #: src/mainw.c:1374 src/program.c:1053 src/program.c:1095 #: src/workspacegroup.c:178 src/workspacegroup.c:215 msgid "Caption" msgstr "Malkovich" #: src/pathname.c:84 src/slider.c:57 src/option.c:71 src/string.c:70 #: src/toggle.c:51 src/clock.c:193 src/number.c:68 src/fontname.c:61 #: src/colour.c:302 src/matrix.c:260 src/plot.c:340 msgid "Value" msgstr "Malkovich" #: src/iarrow.c:117 msgid "No image" msgstr "Malkovich" #. Used in (eg.) "Mark at (10, 10) on [A1, A2]" #. #. Expands to (eg.) "Region on A1 at (10, 10), size (50, 50)" #. #: src/iarrow.c:130 src/iregion.c:156 msgid "on" msgstr "Malkovich" #: src/iarrow.c:148 src/iarrow.c:153 #, fuzzy, c-format msgid "at %d" msgstr "Malkovich" #: src/iarrow.c:158 #, fuzzy, c-format msgid "at (%d, %d)" msgstr "Malkovich" #: src/iarrow.c:165 #, fuzzy, c-format msgid "at (%d, %d), offset (%d, %d)" msgstr "Malkovich" #: src/vips_call.c:174 src/vips_call.c:1106 src/vipsobject.c:284 #: src/util.c:191 msgid "VIPS library error." msgstr "Malkovich" #: src/vips_call.c:175 src/vips_call.c:1109 #, fuzzy, c-format msgid "Error calling library function \"%s\" (%s)." msgstr "Malkovich" #: src/vips_call.c:190 msgid "Unknown type." msgstr "Malkovich" #: src/vips_call.c:191 #, fuzzy, c-format msgid "VIPS type \"%s\" not supported" msgstr "Malkovich" #: src/vips_call.c:955 src/vips_call.c:975 src/class.c:854 msgid "You passed:" msgstr "Malkovich" #: src/vips_call.c:1028 msgid "Usage:" msgstr "Malkovich" #: src/vips_call.c:1030 #, fuzzy, c-format msgid "VIPS operator \"%s\"" msgstr "Malkovich" #: src/vips_call.c:1032 #, fuzzy, c-format msgid "%s, from package \"%s\"" msgstr "Malkovich" #: src/vips_call.c:1037 #, fuzzy, c-format msgid "\"%s\" takes %d argument:" msgid_plural "\"%s\" takes %d arguments:" msgstr[0] "Malkovich" msgstr[1] "Malkovich" #: src/vips_call.c:1044 #, fuzzy, c-format msgid "And produces %d result:" msgid_plural "And produces %d results:" msgstr[0] "Malkovich" msgstr[1] "Malkovich" #. Print any flags this function has. #. #: src/vips_call.c:1052 msgid "Flags:" msgstr "Malkovich" #: src/vips_call.c:1056 msgid "PIO function" msgstr "Malkovich" #: src/vips_call.c:1058 msgid "WIO function" msgstr "Malkovich" #: src/vips_call.c:1061 msgid "coordinate transformer" msgstr "Malkovich" #: src/vips_call.c:1063 msgid "no coordinate transformation" msgstr "Malkovich" #: src/vips_call.c:1066 msgid "point-to-point operation" msgstr "Malkovich" #: src/vips_call.c:1068 msgid "area operation" msgstr "Malkovich" #: src/vips_call.c:1071 msgid "uncacheable operation" msgstr "Malkovich" #: src/vips_call.c:1073 msgid "operation can be cached" msgstr "Malkovich" #: src/vips_call.c:1085 src/action.c:173 src/reduce.c:878 src/reduce.c:891 #: src/builtin.c:990 src/class.c:49 src/class.c:1019 msgid "Bad argument." msgstr "Malkovich" #: src/vips_call.c:1088 #, fuzzy, c-format msgid "Argument %d (%s) to \"%s\" is the wrong type." msgstr "Malkovich" #: src/vips_call.c:1112 #, fuzzy, c-format msgid "VIPS library: %s" msgstr "Malkovich" #: src/vips_call.c:2166 src/vips_call.c:2215 #, fuzzy, c-format msgid "image \"%s\"" msgstr "Malkovich" #: src/vips_call.c:2173 msgid "no image" msgstr "Malkovich" #: src/vips_call.c:2189 msgid "doublevec" msgstr "Malkovich" #: src/vips_call.c:2212 msgid "imagevec" msgstr "Malkovich" #: src/matrixview.c:105 src/gtkutil.c:663 src/gtkutil.c:671 msgid "Bad floating point number." msgstr "Malkovich" #: src/matrixview.c:106 src/gtkutil.c:664 #, fuzzy, c-format msgid "\"%s\" is not a floating point number." msgstr "Malkovich" #: src/matrixview.c:198 src/matrixview.c:219 src/classmodel.c:1161 #: src/plot.c:140 src/plot.c:150 src/plot.c:360 src/plot.c:379 src/plot.c:392 msgid "Bad value." msgstr "Malkovich" #: src/matrixview.c:199 src/matrixview.c:220 #, fuzzy, c-format msgid "" "Cell (%d, %d):\n" "%s" msgstr "Malkovich" #: src/matrixview.c:434 src/matrix.c:263 msgid "Scale" msgstr "Malkovich" #: src/matrixview.c:439 src/matrix.c:266 msgid "Offset" msgstr "Malkovich" #. Expands to (eg) "A2: cell (1,2): 45" ... status line display during #. * matrix traverse. #. #: src/matrixview.c:649 #, fuzzy, c-format msgid ": cell (%d, %d): %s" msgstr "Malkovich" #: src/slider.c:51 msgid "From" msgstr "Malkovich" #: src/slider.c:54 msgid "To" msgstr "Malkovich" #. Need one menu per image window (could have a single menu for all #. * windows, but then we'd have to set the state of the toggle buttons #. * before mapping) #. #: src/imagepresent.c:1562 msgid "Ruler menu" msgstr "Malkovich" #: src/imagepresent.c:1563 msgid "Rulers In _mm" msgstr "Malkovich" #: src/imagepresent.c:1565 msgid "Show _Offset" msgstr "Malkovich" #: src/option.c:68 msgid "Labels" msgstr "Malkovich" #: src/filesel.c:86 msgid "Workspace files (*.ws)" msgstr "Malkovich" #: src/filesel.c:88 msgid "Recombination matrix files (*.rec)" msgstr "Malkovich" #: src/filesel.c:90 msgid "Morphology matrix files (*.mor)" msgstr "Malkovich" #: src/filesel.c:92 msgid "Convolution matrix files (*.con)" msgstr "Malkovich" #: src/filesel.c:94 msgid "Matrix files (*.mat)" msgstr "Malkovich" #: src/filesel.c:96 msgid "Definition files (*.def)" msgstr "Malkovich" #: src/filesel.c:98 msgid "ICC profiles (*.icc, *.icm)" msgstr "Malkovich" #: src/filesel.c:100 msgid "All files (*)" msgstr "Malkovich" #. Used as eg. "VIPS image files (*.v)" #. #: src/filesel.c:133 #, fuzzy msgid "image files" msgstr "Malkovich" #: src/filesel.c:500 #, fuzzy, c-format msgid "Unable to determine space free in \"%s\"." msgstr "Malkovich" #. Expands to (eg.) '6GB free in "/pics/tmp"' #. #: src/filesel.c:511 #, fuzzy, c-format msgid "free in \"%s\"" msgstr "Malkovich" #: src/filesel.c:759 src/util.c:1932 src/util.c:1938 msgid "Bad filename." msgstr "Malkovich" #: src/filesel.c:760 msgid "No file selected." msgstr "Malkovich" #: src/filesel.c:990 msgid "Increment filename" msgstr "Malkovich" #: src/filesel.c:996 msgid "After Save, add 1 to the last number in the file name" msgstr "Malkovich" #: src/filesel.c:1235 #, fuzzy, c-format msgid "%s Save Preferences" msgstr "Malkovich" #: src/filesel.c:1296 msgid "Overwrite" msgstr "Malkovich" #: src/filesel.c:1297 msgid "Overwrite file?" msgstr "Malkovich" #: src/filesel.c:1298 #, fuzzy, c-format msgid "File \"%s\" exists. OK to overwrite?" msgstr "Malkovich" #: src/gtkutil.c:608 msgid "Bad identifier." msgstr "Malkovich" #: src/gtkutil.c:610 msgid "" "Enter an identifier. Identifiers start with a letter, and then contain only " "letters, numbers, apostrophy and underscore." msgstr "Malkovich" #: src/gtkutil.c:672 #, fuzzy, c-format msgid "Extra characters \"%s\" after number." msgstr "Malkovich" #: src/gtkutil.c:696 msgid "Bad integer." msgstr "Malkovich" #: src/gtkutil.c:697 #, fuzzy, c-format msgid "\"%s\" is not an integer." msgstr "Malkovich" #: src/gtkutil.c:715 msgid "Bad unsigned integer." msgstr "Malkovich" #: src/gtkutil.c:731 msgid "Bad positive integer." msgstr "Malkovich" #: src/gtkutil.c:853 src/toggleview.c:106 msgid "Left-click to change value" msgstr "Malkovich" #: src/vipsobject.c:94 #, fuzzy msgid "No such type." msgstr "Malkovich" #: src/vipsobject.c:95 #, fuzzy, c-format msgid "Type \"%s\" not found as a subclass of VipsObject." msgstr "Malkovich" #: src/vipsobject.c:125 src/compile.c:1767 src/class.c:657 src/class.c:881 #: src/class.c:978 msgid "Too many arguments." msgstr "Malkovich" #: src/vipsobject.c:126 #, fuzzy, c-format msgid "No more than %d arguments allowed." msgstr "Malkovich" #: src/vipsobject.c:253 msgid "Wrong number of required arguments." msgstr "Malkovich" #: src/vipsobject.c:254 #, fuzzy, c-format msgid "Operation \"%s\" has %d required arguments, you supplied %d." msgstr "Malkovich" #: src/vipsobject.c:285 #, fuzzy msgid "Unable to build object." msgstr "Malkovich" #: src/util.c:211 msgid "Unable to set XML property." msgstr "Malkovich" #: src/util.c:212 #, fuzzy, c-format msgid "Unable to set property \"%s\" to value \"%s\"." msgstr "Malkovich" #: src/util.c:830 msgid "(no image)" msgstr "Malkovich" #: src/util.c:844 #, fuzzy, c-format msgid "%dx%d %s, %d band, %s" msgid_plural "%dx%d %s, %d bands, %s" msgstr[0] "Malkovich" msgstr[1] "Malkovich" #: src/util.c:1257 msgid "8-bit unsigned integer" msgstr "Malkovich" #. IM_BANDFMT_UCHAR #: src/util.c:1258 msgid "8-bit signed integer" msgstr "Malkovich" #. IM_BANDFMT_CHAR #: src/util.c:1259 msgid "16-bit unsigned integer" msgstr "Malkovich" #. IM_BANDFMT_USHORT #: src/util.c:1260 msgid "16-bit signed integer" msgstr "Malkovich" #. IM_BANDFMT_SHORT #: src/util.c:1261 msgid "32-bit unsigned integer" msgstr "Malkovich" #. IM_BANDFMT_UINT #: src/util.c:1262 msgid "32-bit signed integer" msgstr "Malkovich" #. IM_BANDFMT_INT #: src/util.c:1263 msgid "32-bit float" msgstr "Malkovich" #. IM_BANDFMT_FLOAT #: src/util.c:1264 msgid "64-bit complex" msgstr "Malkovich" #. IM_BANDFMT_COMPLEX #: src/util.c:1265 msgid "64-bit float" msgstr "Malkovich" #. IM_BANDFMT_DOUBLE #: src/util.c:1266 msgid "128-bit complex" msgstr "Malkovich" #: src/util.c:1274 msgid "" msgstr "Malkovich" #: src/util.c:1316 msgid "" msgstr "Malkovich" #: src/util.c:1340 msgid "directory" msgstr "Malkovich" #: src/util.c:1349 src/dump.c:189 msgid "workspace" msgstr "Malkovich" #: src/util.c:1599 msgid "Unable to find install area." msgstr "Malkovich" #: src/util.c:1775 msgid "" msgstr "Malkovich" #: src/util.c:1933 msgid "Filename is too long." msgstr "Malkovich" #: src/util.c:1939 msgid "Filename contains only blank characters." msgstr "Malkovich" #: src/util.c:2002 src/util.c:2011 src/util.c:2066 msgid "Unable to open." msgstr "Malkovich" #: src/util.c:2003 src/util.c:2012 #, fuzzy, c-format msgid "" "Unable to open file \"%s\" for reading.\n" "%s." msgstr "Malkovich" #: src/util.c:2067 #, fuzzy, c-format msgid "" "Unable to open file \"%s\" for writing.\n" "%s." msgstr "Malkovich" #: src/util.c:2088 msgid "Unable to write." msgstr "Malkovich" #: src/util.c:2089 #, fuzzy, c-format msgid "" "Unable to write to file \"%s\".\n" "%s." msgstr "Malkovich" #: src/util.c:2124 src/util.c:2137 src/util.c:2162 msgid "Unable to read." msgstr "Malkovich" #: src/util.c:2125 #, fuzzy, c-format msgid "File \"%s\" too large." msgstr "Malkovich" #: src/util.c:2138 src/util.c:2163 #, fuzzy, c-format msgid "" "Unable to read from file \"%s\".\n" "%s." msgstr "Malkovich" #. File length unit. #. #: src/util.c:2454 msgid "bytes" msgstr "Malkovich" #. Kilo byte unit. #. #: src/util.c:2458 msgid "KB" msgstr "Malkovich" #. Mega byte unit. #. #: src/util.c:2462 msgid "MB" msgstr "Malkovich" #. Giga byte unit. #. #: src/util.c:2466 msgid "GB" msgstr "Malkovich" #. Tera byte unit. #. #: src/util.c:2470 msgid "TB" msgstr "Malkovich" #: src/util.c:2513 msgid "Unable to create temporary file." msgstr "Malkovich" #: src/util.c:2514 #, fuzzy, c-format msgid "" "Unable to make file \"%s\"\n" "%s" msgstr "Malkovich" #: src/util.c:2721 msgid "Out of memory." msgstr "Malkovich" #: src/util.c:2722 #, fuzzy, c-format msgid "Request for %s of RAM triggered memory allocation failure." msgstr "Malkovich" #: src/view.c:820 src/classmodel.c:154 src/classmodel.c:227 src/builtin.c:410 #: src/mainw.c:739 src/mainw.c:747 src/model.c:313 src/model.c:368 #: src/filemodel.c:100 src/filemodel.c:130 src/filemodel.c:342 #: src/filemodel.c:566 src/filemodel.c:607 src/rowview.c:456 msgid "Not implemented." msgstr "Malkovich" #: src/trace.c:126 src/reduce.c:128 src/reduce.c:1036 src/workspace.c:1597 msgid "Overflow error." msgstr "Malkovich" #: src/trace.c:127 msgid "Trace buffer stack overflow." msgstr "Malkovich" #: src/trace.c:238 src/error.c:123 msgid "_Clear" msgstr "Malkovich" #: src/trace.c:239 msgid "Clear trace window" msgstr "Malkovich" #: src/trace.c:243 src/imageview.c:499 src/mainw.c:1662 src/program.c:1700 #: src/plotwindow.c:175 src/error.c:138 msgid "_Close" msgstr "Malkovich" #: src/trace.c:244 msgid "Close trace window" msgstr "Malkovich" #: src/trace.c:248 src/imageview.c:529 src/mainw.c:1732 src/program.c:1780 #: src/plotwindow.c:180 src/error.c:143 msgid "_Contents" msgstr "Malkovich" #: src/trace.c:249 src/imageview.c:530 src/mainw.c:1733 src/program.c:1781 #: src/plotwindow.c:181 src/error.c:144 msgid "Open the users guide" msgstr "Malkovich" #: src/trace.c:253 src/imageview.c:534 src/mainw.c:1727 src/program.c:1775 #: src/plotwindow.c:185 src/error.c:148 msgid "_About" msgstr "Malkovich" #: src/trace.c:254 src/imageview.c:535 src/mainw.c:1728 src/program.c:1776 #: src/plotwindow.c:186 src/error.c:149 msgid "About this program" msgstr "Malkovich" #: src/trace.c:260 msgid "_Operators" msgstr "Malkovich" #: src/trace.c:261 msgid "trace operators" msgstr "Malkovich" #: src/trace.c:265 msgid "_Builtin Functions" msgstr "Malkovich" #: src/trace.c:266 msgid "trace calls to built in functions" msgstr "Malkovich" #: src/trace.c:270 msgid "_Class Construction" msgstr "Malkovich" #: src/trace.c:271 msgid "trace class constructors" msgstr "Malkovich" #: src/trace.c:275 msgid "_VIPS Operations" msgstr "Malkovich" #: src/trace.c:276 msgid "trace calls to VIPS" msgstr "Malkovich" #: src/trace.c:352 msgid "Trace" msgstr "Malkovich" #. Not found? Maybe - error message anyway. #. #: src/symbol.c:486 src/program.c:1440 src/program.c:1489 src/program.c:1511 #: src/program.c:1519 src/path.c:278 src/path.c:305 msgid "Not found." msgstr "Malkovich" #: src/symbol.c:487 #, fuzzy, c-format msgid "Symbol %s is not defined." msgstr "Malkovich" #: src/symbol.c:491 #, fuzzy, c-format msgid "%s is referred to by" msgstr "Malkovich" #: src/symbol.c:707 #, fuzzy, c-format msgid "Redefinition of \"%s\"." msgstr "Malkovich" #: src/symbol.c:711 #, fuzzy, c-format msgid "Previously defined at line %d." msgstr "Malkovich" #: src/symbol.c:734 #, fuzzy, c-format msgid "Attempt to redefine root symbol \"%s\"." msgstr "Malkovich" #. Parameter, workspace, etc. #. #: src/symbol.c:759 #, fuzzy, c-format msgid "Can't redefine %s \"%s\"." msgstr "Malkovich" #: parse.y:244 parse.y:255 msgid "not top level" msgstr "Malkovich" #: parse.y:260 msgid "not strings" msgstr "Malkovich" #: parse.y:318 msgid "left-hand-side pattern contains no identifiers" msgstr "" #: parse.y:1064 src/program.c:1421 msgid "Parse error." msgstr "Malkovich" #: parse.y:1067 #, fuzzy, c-format msgid "Error in %s: %s" msgstr "Malkovich" #: parse.y:1070 #, fuzzy, c-format msgid "Error: %s" msgstr "Malkovich" #: parse.y:1183 #, fuzzy msgid "definition is too long" msgstr "Malkovich" #: parse.y:1607 #, fuzzy msgid "no leading identifier" msgstr "Malkovich" #: parse.y:1613 msgid "'=' missing" msgstr "" #: parse.y:1643 msgid "identifier expected" msgstr "" #: parse.y:1653 #, c-format msgid "'%s' does not exist" msgstr "" #: parse.y:1655 #, c-format msgid "'%s' has no members" msgstr "" #: parse.y:1670 msgid "'.' or '=' expected" msgstr "" #: src/editview.c:158 msgid "Escape to cancel edit, press Return to accept edit and recalculate" msgstr "Malkovich" #: src/classmodel.c:155 src/classmodel.c:228 #, fuzzy, c-format msgid "_%s() method not implemented for %s." msgstr "Malkovich" #: src/classmodel.c:163 #, fuzzy, c-format msgid "Save %s \"%s\"" msgstr "Malkovich" #: src/classmodel.c:237 #, fuzzy, c-format msgid "Replace %s \"%s\"" msgstr "Malkovich" #: src/classmodel.c:695 msgid "Set boolean value here" msgstr "Malkovich" #: src/classmodel.c:701 msgid "Enter a floating point number here" msgstr "Malkovich" #: src/classmodel.c:708 msgid "Enter a string here" msgstr "Malkovich" #. Expands to "Edit Toggle A1". #. #: src/classmodel.c:762 #, fuzzy, c-format msgid "Edit %s %s" msgstr "Malkovich" #: src/classmodel.c:773 #, fuzzy, c-format msgid "Set %s" msgstr "Malkovich" #: src/classmodel.c:1086 #, fuzzy msgid "Unknown option." msgstr "Malkovich" #: src/classmodel.c:1087 #, fuzzy, c-format msgid "Option \"%s\" not known." msgstr "Malkovich" #: src/classmodel.c:1162 #, fuzzy, c-format msgid "%d band value only" msgstr "Malkovich" #: src/heap.c:812 msgid "Heap full." msgstr "Malkovich" #: src/heap.c:818 #, fuzzy, c-format msgid "" "The compile heap for %s has filled. Make it smaller and less complicated." msgstr "Malkovich" #: src/heap.c:823 msgid "" "The main calculation heap has filled. Raise the heap size limit in " "Preferences." msgstr "Malkovich" #: src/heap.c:1837 msgid "Unimplemented list type." msgstr "Malkovich" #: src/heap.c:1932 msgid "Unimplemented type." msgstr "Malkovich" #: src/heap.c:1933 #, fuzzy, c-format msgid "Unable to convert %s to a nip type." msgstr "Malkovich" #: src/action.c:87 msgid "Bad arguments." msgstr "Malkovich" #. Expands to eg. 'bad args to "+", called from "fred"' #. #: src/action.c:102 src/action.c:186 msgid "Called from" msgstr "Malkovich" #: src/action.c:108 #, fuzzy, c-format msgid "" "Error in binary \"%s\".\n" "left = %s\n" "right = %s\n" "%s" msgstr "Malkovich" #: src/action.c:142 src/class.c:190 #, fuzzy, c-format msgid "Member \"%s\" not found in class \"%s\"." msgstr "Malkovich" #: src/action.c:149 #, fuzzy, c-format msgid "object = %s" msgstr "Malkovich" #: src/action.c:153 #, fuzzy, c-format msgid "tag = %s" msgstr "Malkovich" #: src/action.c:158 #, fuzzy, c-format msgid "Reference attempted in \"%s\"." msgstr "Malkovich" #: src/action.c:162 src/class.c:189 msgid "Member not found." msgstr "Malkovich" #: src/action.c:192 #, fuzzy, c-format msgid "" "Error in unary \"%s\".\n" "argument = %s\n" "%s" msgstr "Malkovich" #: src/action.c:359 src/action.c:377 src/action.c:407 #, fuzzy msgid "Bad right hand side of '.'." msgstr "Malkovich" #: src/action.c:369 msgid "Symbol on left hand side of '.' is not scope" msgstr "Malkovich" #: src/action.c:411 #, fuzzy msgid "Property not found." msgstr "Malkovich" #: src/action.c:425 msgid "Bad left hand side of '.'." msgstr "Malkovich" #: src/action.c:937 msgid "Division by zero." msgstr "Malkovich" #: src/action.c:1313 src/action.c:1593 msgid "Unimplemented." msgstr "Malkovich" #: src/action.c:1677 src/action.c:1713 src/action.c:1971 msgid "invoking method:" msgstr "Malkovich" #. Create signals. #. #. Init methods. #. #: src/paintboxview.c:109 msgid "Paintbox bar menu" msgstr "Malkovich" #: src/paintboxview.c:212 msgid "Clear undo history?" msgstr "Malkovich" #: src/paintboxview.c:213 msgid "" "Are you sure you want to clear all undo and redo? This will free up memory, " "but you will no longer be able to undo or redo any of the painting you have " "done so far." msgstr "Malkovich" #: src/paintboxview.c:248 msgid "1 round" msgstr "Malkovich" #. PAINTBOX_1ROUND #: src/paintboxview.c:249 msgid "2 round" msgstr "Malkovich" #. PAINTBOX_2ROUND #: src/paintboxview.c:250 msgid "3 round" msgstr "Malkovich" #. PAINTBOX_3ROUND #: src/paintboxview.c:251 msgid "4 round" msgstr "Malkovich" #. PAINTBOX_4ROUND #: src/paintboxview.c:252 msgid "5 round" msgstr "Malkovich" #. PAINTBOX_5ROUND #: src/paintboxview.c:253 msgid "6 round" msgstr "Malkovich" #. PAINTBOX_6ROUND #: src/paintboxview.c:254 msgid "10 round" msgstr "Malkovich" #. PAINTBOX_10ROUND #: src/paintboxview.c:255 msgid "2 italic" msgstr "Malkovich" #. PAINTBOX_2ITALIC #: src/paintboxview.c:256 msgid "3 italic" msgstr "Malkovich" #. PAINTBOX_3ITALIC #: src/paintboxview.c:257 msgid "4 italic" msgstr "Malkovich" #. PAINTBOX_4ITALIC #: src/paintboxview.c:258 msgid "5 italic" msgstr "Malkovich" #. PAINTBOX_5ITALIC #: src/paintboxview.c:259 msgid "6 italic" msgstr "Malkovich" #. PAINTBOX_6ITALIC #: src/paintboxview.c:260 msgid "10 italic" msgstr "Malkovich" #: src/paintboxview.c:264 msgid "Manipulate regions" msgstr "Malkovich" #. IMAGEMODEL_SELECT #: src/paintboxview.c:265 msgid "Pan window" msgstr "Malkovich" #. IMAGEMODEL_PAN #: src/paintboxview.c:266 msgid "Zoom in on mouse" msgstr "Malkovich" #. IMAGEMODEL_MAGIN #: src/paintboxview.c:267 src/imageview.c:515 src/imageview.c:579 msgid "Zoom out" msgstr "Malkovich" #. IMAGEMODEL_MAGOUT #: src/paintboxview.c:268 msgid "Read pixel into inkwell" msgstr "Malkovich" #. IMAGEMODEL_DROPPER #: src/paintboxview.c:269 msgid "Freehand draw " msgstr "Malkovich" #. IMAGEMODEL_PEN #: src/paintboxview.c:270 msgid "Draw straight lines" msgstr "Malkovich" #. IMAGEMODEL_LINE #: src/paintboxview.c:271 msgid "Fill rectangles" msgstr "Malkovich" #. IMAGEMODEL_RECT #: src/paintboxview.c:272 msgid "Flood while pixel not equal to ink" msgstr "Malkovich" #. IMAGEMODEL_FLOOD #: src/paintboxview.c:274 msgid "Flood while pixel equal to click" msgstr "Malkovich" #. IMAGEMODEL_BLOB #: src/paintboxview.c:276 msgid "Draw text" msgstr "Malkovich" #. IMAGEMODEL_TEXT #: src/paintboxview.c:277 src/main.c:683 msgid "Smudge" msgstr "Malkovich" #: src/paintboxview.c:327 msgid "Undo last paint action" msgstr "Malkovich" #: src/paintboxview.c:336 msgid "Redo last paint action" msgstr "Malkovich" #: src/paintboxview.c:345 msgid "Clear all undo and redo buffers" msgstr "Malkovich" #: src/paintboxview.c:365 msgid "Nib" msgstr "Malkovich" #: src/paintboxview.c:386 msgid "Enter text for text tool" msgstr "Malkovich" #: src/clock.c:59 src/clock.c:93 src/clock.c:190 msgid "Interval" msgstr "Malkovich" #: src/clock.c:60 src/clock.c:96 msgid "Elapsed time" msgstr "Malkovich" #: src/clock.c:93 msgid "Interval between ticks (seconds)" msgstr "Malkovich" #: src/clock.c:96 msgid "Elapsed time (seconds)" msgstr "Malkovich" #: src/clock.c:100 #, fuzzy, c-format msgid "Edit Clock \"%s\"" msgstr "Malkovich" #: src/clock.c:104 msgid "Set Clock" msgstr "Malkovich" #: src/workspacedefs.c:97 #, fuzzy, c-format msgid "%d definition" msgid_plural "%d definitions" msgstr[0] "Malkovich" msgstr[1] "Malkovich" #: src/workspacedefs.c:103 #, fuzzy msgid "errors" msgstr "Malkovich" #: src/workspacedefs.c:108 src/program.c:263 msgid "modified" msgstr "Malkovich" #: src/workspacedefs.c:181 #, fuzzy msgid "Replace Definition From File" msgstr "Malkovich" #: src/workspacedefs.c:261 #, fuzzy msgid "Workspace definitions" msgstr "Malkovich" #: src/workspacedefs.c:262 src/rowview.c:594 msgid "Replace From _File" msgstr "Malkovich" #: src/workspacedefs.c:282 #, fuzzy msgid "Process" msgstr "Malkovich" #: lex.l:91 lex.l:101 msgid "line too long" msgstr "Malkovich" #: lex.l:109 msgid "end of line inside string" msgstr "Malkovich" #: lex.l:111 msgid "no end of string" msgstr "Malkovich" #: lex.l:132 lex.l:142 lex.l:150 msgid "bad char constant" msgstr "Malkovich" #: lex.l:175 #, fuzzy msgid "nested comment" msgstr "Malkovich" #: lex.l:181 #, fuzzy msgid "no end of comment" msgstr "Malkovich" #: lex.l:311 #, fuzzy, c-format msgid "bad number %s" msgstr "Malkovich" #: lex.l:364 #, fuzzy, c-format msgid "illegal character \"%c\"" msgstr "Malkovich" #: src/formula.c:143 msgid "" "Press Escape to cancel edit, press Return to accept edit and recalculate" msgstr "Malkovich" #: src/toolkitbrowser.c:242 msgid "Search:" msgstr "Malkovich" #: src/toolkitbrowser.c:267 msgid "Action" msgstr "Malkovich" #: src/toolkitbrowser.c:275 msgid "Parameters" msgstr "Malkovich" #: src/toolkitbrowser.c:283 msgid "Menu Item" msgstr "Malkovich" #: src/main.c:112 msgid "evaluate and print EXPRESSION" msgstr "Malkovich" #: src/main.c:115 msgid "load FILE as a set of definitions" msgstr "Malkovich" #: src/main.c:118 msgid "write value of 'main' to FILE" msgstr "Malkovich" #: src/main.c:120 msgid "run in batch mode" msgstr "Malkovich" #: src/main.c:122 #, fuzzy msgid "set values" msgstr "Malkovich" #: src/main.c:124 msgid "verbose error output" msgstr "" #: src/main.c:127 msgid "don't load menu definitions" msgstr "Malkovich" #: src/main.c:129 #, fuzzy msgid "don't try to load command-line arguments" msgstr "Malkovich" #: src/main.c:131 msgid "load stdin as a workspace" msgstr "Malkovich" #: src/main.c:133 msgid "load stdin as a set of definitions" msgstr "Malkovich" #: src/main.c:135 msgid "print value of 'main' to stdout" msgstr "Malkovich" #: src/main.c:138 msgid "start up and shut down" msgstr "Malkovich" #: src/main.c:141 msgid "time image save operations" msgstr "Malkovich" #: src/main.c:144 msgid "start as if installed to PREFIX" msgstr "" #: src/main.c:146 msgid "output strings for internationalisation" msgstr "Malkovich" #: src/main.c:149 msgid "print version number" msgstr "Malkovich" #: src/main.c:152 msgid "test for errors and quit" msgstr "" #: src/main.c:231 #, fuzzy, c-format msgid "error calculating \"%s\"" msgstr "Malkovich" #: src/main.c:239 #, fuzzy, c-format msgid "error saving \"%s\"" msgstr "Malkovich" #: src/main.c:293 #, fuzzy msgid "no \"main\" found" msgstr "Malkovich" #: src/main.c:456 msgid "Unknown file type." msgstr "Malkovich" #: src/main.c:457 #, fuzzy, c-format msgid "Unable to load \"%s\"." msgstr "Malkovich" #: src/main.c:468 msgid "Unable to load." msgstr "Malkovich" #: src/main.c:469 #, fuzzy, c-format msgid "Error loading plug-in \"%s\"." msgstr "Malkovich" #: src/main.c:677 msgid "Next _Error" msgstr "Malkovich" #: src/main.c:678 msgid "Ink dropper" msgstr "Malkovich" #: src/main.c:679 msgid "D_uplicate" msgstr "Malkovich" #: src/main.c:680 msgid "Pen" msgstr "Malkovich" #: src/main.c:681 src/plot.c:94 msgid "Line" msgstr "Malkovich" #: src/main.c:682 src/matrix.c:147 msgid "Text" msgstr "Malkovich" #: src/main.c:684 msgid "Flood" msgstr "Malkovich" #: src/main.c:685 msgid "Flood Blob" msgstr "Malkovich" #: src/main.c:686 msgid "Fill Rectangle" msgstr "Malkovich" #: src/main.c:687 msgid "Pan" msgstr "Malkovich" #: src/main.c:688 msgid "Select" msgstr "Malkovich" #. And the LEDs we use. #. #: src/main.c:692 msgid "Red LED" msgstr "Malkovich" #: src/main.c:693 msgid "Green LED" msgstr "Malkovich" #: src/main.c:694 msgid "Blue LED" msgstr "Malkovich" #: src/main.c:695 msgid "Yellow LED" msgstr "Malkovich" #: src/main.c:696 msgid "Cyan LED" msgstr "Malkovich" #: src/main.c:697 msgid "Off LED" msgstr "Malkovich" #: src/main.c:884 msgid "Empty temp area" msgstr "Malkovich" #: src/main.c:885 msgid "Many files in temp area." msgstr "Malkovich" #: src/main.c:886 #, fuzzy, c-format msgid "" "The temp area \"%s\" contains %s of files. Would you like to empty the temp " "area? This will delete any workspace backups and cannot be undone." msgstr "Malkovich" #: src/main.c:902 #, fuzzy, c-format msgid "unable to make %s %s: %s" msgstr "Malkovich" #: src/main.c:1080 msgid "- image processing spreadsheet" msgstr "Malkovich" #: src/main.c:1112 #, fuzzy, c-format msgid "linked to vips-%s" msgstr "Malkovich" #. -1 means can't-be-set, at least on os x, so don't #. * warn. #. #: src/main.c:1172 #, fuzzy, c-format msgid "" "unable to change max file descriptors\n" "max file descriptors still set to %d" msgstr "Malkovich" #: src/main.c:1178 msgid "unable to read max file descriptors" msgstr "Malkovich" #: src/main.c:1402 src/main.c:1455 #, fuzzy, c-format msgid "" "Startup error log:\n" "%s" msgstr "Malkovich" #: src/main.c:1454 msgid "Startup error." msgstr "Malkovich" #: src/main.c:1465 #, fuzzy, c-format msgid "Welcome to %s-%s!" msgstr "Malkovich" #: src/main.c:1470 #, fuzzy, c-format msgid "" "A new directory has been created in your home directory to hold startup, " "data and temporary files:\n" "\n" " %s\n" "\n" "If you've used previous versions of %s, you will probably want to move any " "files over from your old work area and remove any old temps." msgstr "Malkovich" #: src/conversion.c:464 msgid "not uncoded" msgstr "Malkovich" #: src/conversion.c:1406 #, fuzzy, c-format msgid "Header for \"%s\"" msgstr "Malkovich" #: src/conversion.c:1408 msgid "OK" msgstr "Malkovich" #: src/reduce.c:115 msgid "Typecheck error." msgstr "Malkovich" #: src/reduce.c:117 #, fuzzy, c-format msgid "%s expected %s, instead saw:" msgstr "Malkovich" #: src/reduce.c:129 #, fuzzy, c-format msgid "%s too long." msgstr "Malkovich" #: src/reduce.c:512 src/reduce.c:577 msgid "Not rectangular." msgstr "Malkovich" #: src/reduce.c:513 src/reduce.c:578 #, fuzzy, c-format msgid "" "Matrix of real is not rectangular. Found row of length %d, should be %d." msgstr "Malkovich" #: src/reduce.c:537 msgid "Zero dimension." msgstr "" #: src/reduce.c:538 #, c-format msgid "Matrix has width %d, height %d." msgstr "" #: src/reduce.c:830 msgid "List length" msgstr "Malkovich" #: src/reduce.c:879 #, fuzzy, c-format msgid "List index must be positive, not %d" msgstr "Malkovich" #: src/reduce.c:887 msgid "List index" msgstr "Malkovich" #: src/reduce.c:892 #, fuzzy, c-format msgid "List only has %d elements, unable to get element %d." msgstr "Malkovich" #: src/reduce.c:935 msgid "No arguments allowed." msgstr "Malkovich" #: src/reduce.c:936 #, fuzzy, c-format msgid "Object \"%s\" should have no arguments." msgstr "Malkovich" #: src/reduce.c:1037 msgid "C stack overflow. Expression too complex." msgstr "Malkovich" #: src/reduce.c:1053 #, fuzzy msgid "Cancelled." msgstr "Malkovich" #: src/reduce.c:1054 msgid "Evaluation cancelled." msgstr "" #: src/reduce.c:1139 msgid "No value." msgstr "Malkovich" #: src/reduce.c:1140 #, fuzzy, c-format msgid "Symbol \"%s\" has no value." msgstr "Malkovich" #: src/reduce.c:1612 src/reduce.c:1617 src/reduce.c:1623 msgid "List generator" msgstr "Malkovich" #: src/builtin.c:257 msgid "Out of range." msgstr "Malkovich" #: src/builtin.c:258 msgid "gammq arguments must be a > 0, x >= 0." msgstr "Malkovich" #: src/builtin.c:265 msgid "Not available." msgstr "Malkovich" #: src/builtin.c:266 msgid "No GSL library available for gammq." msgstr "Malkovich" #: src/builtin.c:411 msgid "Complex math ops not implemented." msgstr "Malkovich" #: src/builtin.c:486 msgid "Macro error." msgstr "Malkovich" #: src/builtin.c:669 #, fuzzy msgid "No such type" msgstr "Malkovich" #: src/builtin.c:670 #, fuzzy, c-format msgid "GType %u not found." msgstr "Malkovich" #: src/builtin.c:887 msgid "GSL library error." msgstr "Malkovich" #: src/builtin.c:936 #, fuzzy, c-format msgid "Builtin \"%s\" takes %d argument." msgid_plural "Builtin \"%s\" takes %d arguments." msgstr[0] "Malkovich" msgstr[1] "Malkovich" #: src/builtin.c:991 #, fuzzy, c-format msgid "" "Argument %d to builtin \"%s\" should be \"%s\", you passed:\n" " %s" msgstr "Malkovich" #: src/conversionview.c:66 msgid "Unable to find image range." msgstr "Malkovich" #: src/conversionview.c:67 msgid "Find image range failed." msgstr "Malkovich" #: src/conversionview.c:90 msgid "Unable to scale image." msgstr "Malkovich" #: src/conversionview.c:91 msgid "Maximum and minimum pixel values are equal." msgstr "Malkovich" #. Build menu. One for each window, as we need to track falsecolour #. * etc. toggles. Could just have one, and modify pre-popup, but this #. * is easier. #. #: src/conversionview.c:229 msgid "Convert menu" msgstr "Malkovich" #: src/conversionview.c:230 msgid "_Scale" msgstr "Malkovich" #: src/conversionview.c:232 msgid "_False Color" msgstr "Malkovich" #: src/conversionview.c:234 msgid "_Interpret" msgstr "Malkovich" #: src/conversionview.c:236 src/regionview.c:1027 msgid "_Reset" msgstr "Malkovich" #: src/conversionview.c:238 msgid "Set As Workspace _Default" msgstr "Malkovich" #: src/progress.c:111 #, fuzzy msgid "Cancelling" msgstr "Malkovich" #: src/progress.c:144 #, fuzzy, c-format msgid "%d minute left" msgid_plural "%d minutes left" msgstr[0] "Malkovich" msgstr[1] "Malkovich" #: src/progress.c:149 #, fuzzy, c-format msgid "%d second left" msgid_plural "%d seconds left" msgstr[0] "Malkovich" msgstr[1] "Malkovich" #: src/progress.c:170 msgid "Calculating" msgstr "Malkovich" #: src/progress.c:189 #, fuzzy msgid "Loading" msgstr "Malkovich" #: src/expr.c:64 #, fuzzy, c-format msgid "error in \"%s\"" msgstr "Malkovich" #: src/expr.c:347 msgid "Error" msgstr "Malkovich" #: src/expr.c:614 msgid "top level" msgstr "Malkovich" #: src/expr.c:619 msgid "class" msgstr "Malkovich" #: src/expr.c:622 msgid "instance" msgstr "Malkovich" #: src/expr.c:626 msgid "definition" msgstr "Malkovich" #: src/expr.c:633 #, fuzzy, c-format msgid "parameter \"%s\"" msgstr "Malkovich" #: src/expr.c:637 msgid "member" msgstr "Malkovich" #: src/expr.c:642 src/dump.c:186 msgid "value" msgstr "Malkovich" #: src/expr.c:646 src/itext.c:446 msgid "function" msgstr "Malkovich" #: src/expr.c:655 msgid "of" msgstr "Malkovich" #: src/imagemodel.c:505 msgid "No text specified." msgstr "Malkovich" #: src/imagemodel.c:506 msgid "Enter some text to paint in the entry widget at the top of the window." msgstr "Malkovich" #: src/imageview.c:459 msgid "New _Mark" msgstr "Malkovich" #: src/imageview.c:460 msgid "Create a new mark" msgstr "Malkovich" #: src/imageview.c:464 msgid "New _Horizontal Guide" msgstr "Malkovich" #: src/imageview.c:465 msgid "Create a new horizontal guide" msgstr "Malkovich" #: src/imageview.c:469 msgid "New _Vertical Guide" msgstr "Malkovich" #: src/imageview.c:470 msgid "Create a new vertical guide" msgstr "Malkovich" #: src/imageview.c:474 msgid "New _Arrow" msgstr "Malkovich" #: src/imageview.c:475 msgid "Create a new arrow" msgstr "Malkovich" #: src/imageview.c:479 msgid "New _Region" msgstr "Malkovich" #: src/imageview.c:480 msgid "Create a new region" msgstr "Malkovich" #: src/imageview.c:484 msgid "Replace Image" msgstr "Malkovich" #: src/imageview.c:485 msgid "Replace image from file" msgstr "Malkovich" #: src/imageview.c:489 msgid "Save Image As" msgstr "Malkovich" #: src/imageview.c:490 msgid "Save image to file" msgstr "Malkovich" #: src/imageview.c:494 src/mainw.c:853 msgid "Recalculate" msgstr "Malkovich" #: src/imageview.c:495 msgid "Recalculate image" msgstr "Malkovich" #: src/imageview.c:500 src/program.c:1701 src/plotwindow.c:176 msgid "Close" msgstr "Malkovich" #: src/imageview.c:504 msgid "Image _Header" msgstr "Malkovich" #: src/imageview.c:505 msgid "View image header" msgstr "Malkovich" #: src/imageview.c:509 src/imageview.c:573 msgid "Zoom _In" msgstr "Malkovich" #: src/imageview.c:510 src/imageview.c:574 msgid "Zoom in on mouse cursor" msgstr "Malkovich" #: src/imageview.c:514 src/imageview.c:578 msgid "Zoom _Out" msgstr "Malkovich" #: src/imageview.c:519 msgid "Zoom _100%" msgstr "Malkovich" #: src/imageview.c:520 src/imageview.c:593 msgid "Zoom to 100%" msgstr "Malkovich" #: src/imageview.c:524 msgid "Zoom to _Fit" msgstr "Malkovich" #: src/imageview.c:525 msgid "Zoom to fit image to window" msgstr "Malkovich" #: src/imageview.c:541 src/plotwindow.c:192 msgid "_Status" msgstr "Malkovich" #: src/imageview.c:542 src/plotwindow.c:193 msgid "Show status bar" msgstr "Malkovich" #: src/imageview.c:546 msgid "_Display Control" msgstr "Malkovich" #: src/imageview.c:547 msgid "Show display control bar" msgstr "Malkovich" #: src/imageview.c:551 msgid "_Paint" msgstr "Malkovich" #: src/imageview.c:552 msgid "Show paint bar" msgstr "Malkovich" #: src/imageview.c:556 msgid "_Rulers" msgstr "Malkovich" #: src/imageview.c:557 msgid "Show rulers" msgstr "Malkovich" #: src/imageview.c:563 msgid "_Select" msgstr "Malkovich" #: src/imageview.c:564 msgid "Select and modify selections" msgstr "Malkovich" #: src/imageview.c:568 msgid "_Pan" msgstr "Malkovich" #: src/imageview.c:569 msgid "Pan image" msgstr "Malkovich" #: src/imageview.c:585 msgid "6%" msgstr "Malkovich" #: src/imageview.c:585 msgid "Zoom to 6%" msgstr "Malkovich" #: src/imageview.c:587 msgid "12%" msgstr "Malkovich" #: src/imageview.c:587 msgid "Zoom to 12%" msgstr "Malkovich" #: src/imageview.c:589 msgid "25%" msgstr "Malkovich" #: src/imageview.c:589 msgid "Zoom to 25%" msgstr "Malkovich" #: src/imageview.c:591 msgid "50%" msgstr "Malkovich" #: src/imageview.c:591 msgid "Zoom to 50%" msgstr "Malkovich" #: src/imageview.c:593 msgid "100%" msgstr "Malkovich" #: src/imageview.c:595 msgid "200%" msgstr "Malkovich" #: src/imageview.c:595 msgid "Zoom to 200%" msgstr "Malkovich" #: src/imageview.c:597 msgid "400%" msgstr "Malkovich" #: src/imageview.c:597 msgid "Zoom to 400%" msgstr "Malkovich" #: src/imageview.c:599 msgid "800%" msgstr "Malkovich" #: src/imageview.c:599 msgid "Zoom to 800%" msgstr "Malkovich" #: src/imageview.c:601 msgid "1600%" msgstr "Malkovich" #: src/imageview.c:601 msgid "Zoom to 1600%" msgstr "Malkovich" #: src/colour.c:261 #, fuzzy, c-format msgid "Edit Color \"%s\"" msgstr "Malkovich" #: src/colour.c:267 msgid "Set Color" msgstr "Malkovich" #: src/colour.c:299 #, fuzzy msgid "Color Space" msgstr "Malkovich" #: src/mainw.c:169 msgid "Compatibility mode." msgstr "Malkovich" #: src/mainw.c:170 #, fuzzy, c-format msgid "" "This workspace was created by version %d.%d.%d. A set of compatibility menus " "have been loaded for this window." msgstr "Malkovich" #: src/mainw.c:342 msgid "No temp area" msgstr "Malkovich" #: src/mainw.c:348 #, fuzzy, c-format msgid "%s free" msgstr "Malkovich" #: src/mainw.c:361 #, fuzzy, c-format msgid "%d cells free" msgstr "Malkovich" #. Display select message instead. #. #: src/mainw.c:376 msgid "Selected:" msgstr "Malkovich" #: src/mainw.c:406 msgid "compatibility mode" msgstr "Malkovich" #: src/mainw.c:415 #, fuzzy msgid "unsaved workspace" msgstr "Malkovich" #. Expands to (eg.) "14GB free in /pics/tmp" #: src/mainw.c:596 #, fuzzy, c-format msgid " in \"%s\"" msgstr "Malkovich" #: src/mainw.c:600 #, fuzzy, c-format msgid "%d cells in heap, %d cells free, %d cells maximum" msgstr "Malkovich" #: src/mainw.c:604 #, fuzzy, c-format msgid "%d objects in workspace" msgstr "Malkovich" #: src/mainw.c:608 #, fuzzy, c-format msgid "%d vips calls cached" msgstr "Malkovich" #: src/mainw.c:740 msgid "Find in workspace not implemented yet." msgstr "Malkovich" #: src/mainw.c:748 msgid "Find again in workspace not implemented yet." msgstr "Malkovich" #: src/mainw.c:783 msgid "No errors." msgstr "Malkovich" #: src/mainw.c:784 msgid "There are no errors (that I can see) in this workspace." msgstr "Malkovich" #: src/mainw.c:854 msgid "Completely recalculate?" msgstr "Malkovich" #: src/mainw.c:994 src/model.c:130 src/filemodel.c:452 src/filemodel.c:461 #: src/filemodel.c:474 msgid "Load failed." msgstr "Malkovich" #: src/mainw.c:995 #, fuzzy, c-format msgid "" "Unable to execute:\n" " %s" msgstr "Malkovich" #: src/mainw.c:1012 src/mainw.c:1045 msgid "Open File" msgstr "Malkovich" #: src/mainw.c:1159 msgid "Recent Images" msgstr "Malkovich" #: src/mainw.c:1167 msgid "Recent Workspaces" msgstr "Malkovich" #: src/mainw.c:1175 msgid "Recent Matricies" msgstr "Malkovich" #: src/mainw.c:1184 msgid "No recent items" msgstr "Malkovich" #: src/mainw.c:1190 msgid "Clear Recent Menu" msgstr "Malkovich" #: src/mainw.c:1250 #, fuzzy msgid "Merge Workspace from File" msgstr "Malkovich" #: src/mainw.c:1334 src/mainw.c:1372 src/program.c:596 src/program.c:625 #: src/program.c:1052 src/program.c:1093 src/program.c:1139 src/program.c:1171 #: src/program.c:1500 src/program.c:1541 src/program.c:2224 src/row.c:479 #: src/workspacegroup.c:177 src/workspacegroup.c:213 src/columnview.c:180 #: src/columnview.c:241 msgid "Name" msgstr "Malkovich" #: src/mainw.c:1372 #, fuzzy msgid "Set column name here" msgstr "Malkovich" #: src/mainw.c:1374 #, fuzzy msgid "Set column caption here" msgstr "Malkovich" #: src/mainw.c:1376 #, fuzzy msgid "New Column" msgstr "Malkovich" #: src/mainw.c:1380 #, fuzzy msgid "Create Column" msgstr "Malkovich" #: src/mainw.c:1519 msgid "Revert to Defaults" msgstr "Malkovich" #: src/mainw.c:1520 msgid "Revert to installation defaults?" msgstr "Malkovich" #: src/mainw.c:1521 msgid "" "Would you like to reset all preferences to their factory settings? This will " "delete any changes you have ever made to your preferences and may take a few " "seconds." msgstr "Malkovich" #: src/mainw.c:1538 msgid "Preferences" msgstr "Malkovich" #: src/mainw.c:1543 #, fuzzy msgid "Revert to Defaults ..." msgstr "Malkovich" #. Menu items. #. #: src/mainw.c:1595 msgid "_File" msgstr "Malkovich" #: src/mainw.c:1596 msgid "_New" msgstr "Malkovich" #: src/mainw.c:1597 msgid "Open _Recent" msgstr "Malkovich" #: src/mainw.c:1598 src/mainw.c:1722 src/regionview.c:1023 src/program.c:727 #: src/rowview.c:586 msgid "_Edit" msgstr "Malkovich" #: src/mainw.c:1599 src/mainw.c:2085 msgid "Jump to _Column" msgstr "Malkovich" #: src/mainw.c:1600 msgid "_View" msgstr "Malkovich" #: src/mainw.c:1601 msgid "_Toolkits" msgstr "Malkovich" #: src/mainw.c:1602 msgid "_Help" msgstr "Malkovich" #: src/mainw.c:1612 src/mainw.c:2082 msgid "New C_olumn" msgstr "Malkovich" #: src/mainw.c:1613 msgid "Create a new column" msgstr "Malkovich" #: src/mainw.c:1617 #, fuzzy msgid "New Named C_olumn" msgstr "Malkovich" #: src/mainw.c:1618 #, fuzzy msgid "Create a new column with a specified name" msgstr "Malkovich" #: src/mainw.c:1622 src/program.c:1670 msgid "New _Workspace" msgstr "Malkovich" #: src/mainw.c:1623 msgid "Create a new workspace" msgstr "Malkovich" #: src/mainw.c:1627 msgid "_Open" msgstr "Malkovich" #: src/mainw.c:1628 msgid "Open a file" msgstr "Malkovich" #: src/mainw.c:1632 msgid "Open _Examples" msgstr "Malkovich" #: src/mainw.c:1633 msgid "Open example workspaces" msgstr "Malkovich" #: src/mainw.c:1637 msgid "_Duplicate Workspace" msgstr "Malkovich" #: src/mainw.c:1638 msgid "Duplicate workspace" msgstr "Malkovich" #: src/mainw.c:1642 msgid "_Merge Workspace" msgstr "Malkovich" #: src/mainw.c:1643 msgid "Merge workspace into this workspace" msgstr "Malkovich" #: src/mainw.c:1647 msgid "_Save Workspace" msgstr "Malkovich" #: src/mainw.c:1648 msgid "Save workspace" msgstr "Malkovich" #: src/mainw.c:1652 msgid "_Save Workspace As" msgstr "Malkovich" #: src/mainw.c:1653 msgid "Save workspace as" msgstr "Malkovich" #: src/mainw.c:1657 #, fuzzy msgid "Search for Workspace _Backups" msgstr "Malkovich" #: src/mainw.c:1658 msgid "Load last automatically backed-up workspace" msgstr "Malkovich" #: src/mainw.c:1663 msgid "Close workspace" msgstr "Malkovich" #: src/mainw.c:1667 src/program.c:1720 msgid "_Delete" msgstr "Malkovich" #: src/mainw.c:1668 msgid "Delete selected items" msgstr "Malkovich" #: src/mainw.c:1672 src/program.c:1725 src/columnview.c:896 msgid "Select _All" msgstr "Malkovich" #: src/mainw.c:1673 msgid "Select all items" msgstr "Malkovich" #: src/mainw.c:1677 msgid "D_uplicate Selected" msgstr "Malkovich" #: src/mainw.c:1678 msgid "Duplicate selected items" msgstr "Malkovich" #: src/mainw.c:1682 src/rowview.c:596 msgid "_Recalculate" msgstr "Malkovich" #: src/mainw.c:1683 msgid "Recalculate selected items" msgstr "Malkovich" #: src/mainw.c:1687 src/mainw.c:2086 msgid "Align _Columns" msgstr "Malkovich" #: src/mainw.c:1688 msgid "Align columns to grid" msgstr "Malkovich" #: src/mainw.c:1692 src/program.c:1745 msgid "_Find" msgstr "Malkovich" #: src/mainw.c:1693 msgid "Find in workspace" msgstr "Malkovich" #: src/mainw.c:1697 src/program.c:1750 msgid "Find _Next" msgstr "Malkovich" #: src/mainw.c:1698 msgid "Find again in workspace" msgstr "Malkovich" #: src/mainw.c:1703 msgid "Jump to next error" msgstr "Malkovich" #: src/mainw.c:1707 msgid "_Group" msgstr "Malkovich" #: src/mainw.c:1708 msgid "Group selected items" msgstr "Malkovich" #: src/mainw.c:1712 src/rowview.c:590 msgid "U_ngroup" msgstr "Malkovich" #: src/mainw.c:1713 msgid "Ungroup selected items" msgstr "Malkovich" #: src/mainw.c:1717 msgid "_Preferences" msgstr "Malkovich" #: src/mainw.c:1718 msgid "Edit preferences" msgstr "Malkovich" #: src/mainw.c:1723 msgid "Edit toolkits" msgstr "Malkovich" #: src/mainw.c:1737 msgid "_Website" msgstr "Malkovich" #: src/mainw.c:1738 msgid "Open the VIPS Homepage" msgstr "Malkovich" #: src/mainw.c:1744 msgid "Au_to Recalculate" msgstr "Malkovich" #: src/mainw.c:1745 msgid "Recalculate automatically on change" msgstr "Malkovich" #: src/mainw.c:1749 msgid "_Toolbar" msgstr "Malkovich" #: src/mainw.c:1750 msgid "Show window toolbar" msgstr "Malkovich" #: src/mainw.c:1754 msgid "_Statusbar" msgstr "Malkovich" #: src/mainw.c:1755 msgid "Show window statusbar" msgstr "Malkovich" #: src/mainw.c:1759 msgid "Toolkit _Browser" msgstr "Malkovich" #: src/mainw.c:1760 msgid "Show toolkit browser" msgstr "Malkovich" #: src/mainw.c:1764 #, fuzzy msgid "Workspace _Definitions" msgstr "Malkovich" #: src/mainw.c:1765 #, fuzzy msgid "Show workspace definitions" msgstr "Malkovich" #: src/mainw.c:1771 msgid "_Normal" msgstr "Malkovich" #: src/mainw.c:1772 msgid "Normal view mode" msgstr "Malkovich" #: src/mainw.c:1776 msgid "Show _Formula" msgstr "Malkovich" #: src/mainw.c:1777 msgid "Show formula view mode" msgstr "Malkovich" #: src/mainw.c:1781 msgid "No _Edits" msgstr "Malkovich" #: src/mainw.c:1782 msgid "No edits view mode" msgstr "Malkovich" #: src/mainw.c:2081 #, fuzzy msgid "Workspace menu" msgstr "Malkovich" #: src/mainw.c:2091 #, fuzzy msgid "_Merge Workspace from File" msgstr "Malkovich" #: src/mainw.c:2094 #, fuzzy msgid "_Group Selected" msgstr "Malkovich" #. Toolkit Browser pane. #. #: src/mainw.c:2103 #, fuzzy msgid "Toolkit Browser" msgstr "Malkovich" #. Workspace-local defs pane. #. #: src/mainw.c:2122 #, fuzzy msgid "Workspace Definitions" msgstr "Malkovich" #. Probably failed to load prefs on startup for some reason. #. #: src/prefs.c:207 msgid "Unable to display preferences." msgstr "Malkovich" #: src/prefs.c:208 #, fuzzy, c-format msgid "" "No preferences workspace was found. Preferences probably failed to load when " "%s started." msgstr "Malkovich" #: src/regionview.c:942 src/rowview.c:302 msgid "Can't duplicate." msgstr "Malkovich" #: src/regionview.c:944 msgid "You can only duplicate top level regions." msgstr "Malkovich" #: src/regionview.c:986 msgid "Can't delete." msgstr "Malkovich" #: src/regionview.c:987 msgid "You can only delete top level regions." msgstr "Malkovich" #: src/regionview.c:996 msgid "Delete Region?" msgstr "Malkovich" #: src/regionview.c:997 #, fuzzy, c-format msgid "Are you sure you want to delete Region \"%s\"?" msgstr "Malkovich" #. Other init. #. #: src/regionview.c:1022 msgid "Region menu" msgstr "Malkovich" #: src/program.c:73 msgid "Edit window" msgstr "Malkovich" #: src/program.c:597 src/program.c:627 src/program.c:1140 src/program.c:1173 #: src/matrix.c:269 src/columnview.c:182 src/columnview.c:245 msgid "Filename" msgstr "Malkovich" #: src/program.c:625 msgid "Menu item text" msgstr "Malkovich" #: src/program.c:628 msgid "Load column from this file" msgstr "Malkovich" #: src/program.c:630 #, fuzzy, c-format msgid "Edit Column Item \"%s\"" msgstr "Malkovich" #: src/program.c:635 msgid "Set column item" msgstr "Malkovich" #: src/program.c:656 src/program.c:662 msgid "Unable to save." msgstr "Malkovich" #: src/program.c:657 msgid "You can only save toolkits, not tools." msgstr "Malkovich" #: src/program.c:663 msgid "You can't save auto-generated toolkits." msgstr "Malkovich" #. Create signals. #. #. Init methods. #. #: src/program.c:726 msgid "Toolkit menu" msgstr "Malkovich" #: src/program.c:1093 msgid "Set toolkit name here" msgstr "Malkovich" #: src/program.c:1095 msgid "Set toolkit caption here" msgstr "Malkovich" #: src/program.c:1096 msgid "New Toolkit" msgstr "Malkovich" #: src/program.c:1100 src/program.c:1178 msgid "Create" msgstr "Malkovich" #: src/program.c:1111 msgid "Nothing selected." msgstr "Malkovich" #: src/program.c:1112 msgid "No toolkit selected." msgstr "Malkovich" #: src/program.c:1171 msgid "Display this name" msgstr "Malkovich" #: src/program.c:1173 msgid "Load this file" msgstr "Malkovich" #: src/program.c:1241 msgid "Load Definition" msgstr "Malkovich" #: src/program.c:1295 msgid "Reload" msgstr "Malkovich" #: src/program.c:1296 msgid "Reload startup objects?" msgstr "Malkovich" #: src/program.c:1297 msgid "" "Would you like to reload all startup menus, workspaces and plugins now? This " "may take a few seconds." msgstr "Malkovich" #: src/program.c:1379 msgid "No tool selected" msgstr "Malkovich" #: src/program.c:1422 msgid "Bad regular expression." msgstr "Malkovich" #: src/program.c:1441 #, fuzzy, c-format msgid "No match found for \"%s\"." msgstr "Malkovich" #: src/program.c:1454 msgid "Find in all Toolkits" msgstr "Malkovich" #: src/program.c:1464 msgid "Enter search string here" msgstr "Malkovich" #: src/program.c:1512 #, fuzzy, c-format msgid "No top-level symbol called \"%s\"." msgstr "Malkovich" #: src/program.c:1520 #, fuzzy, c-format msgid "Symbol \"%s\" has no tool inforation." msgstr "Malkovich" #: src/program.c:1541 msgid "Go to definition of this symbol" msgstr "Malkovich" #: src/program.c:1543 msgid "Go to Definition" msgstr "Malkovich" #: src/program.c:1573 msgid "Object information." msgstr "Malkovich" #: src/program.c:1624 msgid "No documentation available." msgstr "Malkovich" #: src/program.c:1625 msgid "" "On-line documentation is only currently available for VIPS functions and nip " "builtins." msgstr "Malkovich" #: src/program.c:1645 msgid "New _Tool" msgstr "Malkovich" #: src/program.c:1646 msgid "Make a new tool" msgstr "Malkovich" #: src/program.c:1650 msgid "New Tool_kit" msgstr "Malkovich" #: src/program.c:1651 msgid "Make a new toolkit" msgstr "Malkovich" #: src/program.c:1655 msgid "New _Separator" msgstr "Malkovich" #: src/program.c:1656 msgid "Make a new separator" msgstr "Malkovich" #: src/program.c:1660 msgid "New _Column Item" msgstr "Malkovich" #: src/program.c:1661 msgid "Make a new column item" msgstr "Malkovich" #: src/program.c:1665 msgid "New _Program Window" msgstr "Malkovich" #: src/program.c:1666 msgid "Make a new program window" msgstr "Malkovich" #: src/program.c:1671 msgid "Make a new workspace" msgstr "Malkovich" #: src/program.c:1675 msgid "_Open Toolkit" msgstr "Malkovich" #: src/program.c:1676 msgid "_Open toolkit" msgstr "Malkovich" #: src/program.c:1680 msgid "Save Toolkit" msgstr "Malkovich" #: src/program.c:1681 msgid "_Save toolkit" msgstr "Malkovich" #: src/program.c:1685 msgid "Save Toolkit _As" msgstr "Malkovich" #: src/program.c:1686 msgid "Save toolkit as" msgstr "Malkovich" #: src/program.c:1690 msgid "_Process" msgstr "Malkovich" #: src/program.c:1691 msgid "Process text" msgstr "Malkovich" #: src/program.c:1695 msgid "_Reload Start Stuff" msgstr "Malkovich" #: src/program.c:1696 msgid "Remove and reload all startup data" msgstr "Malkovich" #: src/program.c:1705 msgid "C_ut" msgstr "Malkovich" #: src/program.c:1706 msgid "Cut selected text" msgstr "Malkovich" #: src/program.c:1710 msgid "_Copy" msgstr "Malkovich" #: src/program.c:1711 msgid "Copy selected text" msgstr "Malkovich" #: src/program.c:1715 msgid "_Paste" msgstr "Malkovich" #: src/program.c:1716 msgid "Paste selected text" msgstr "Malkovich" #: src/program.c:1721 msgid "Delete selected text" msgstr "Malkovich" #: src/program.c:1726 msgid "Select all text" msgstr "Malkovich" #: src/program.c:1730 msgid "Dese_lect All" msgstr "Malkovich" #: src/program.c:1731 msgid "Deselect all text" msgstr "Malkovich" #: src/program.c:1735 msgid "Delete _Tool" msgstr "Malkovich" #: src/program.c:1736 msgid "Delete current tool" msgstr "Malkovich" #: src/program.c:1740 msgid "Delete Tool_kit" msgstr "Malkovich" #: src/program.c:1741 msgid "Delete current toolkit" msgstr "Malkovich" #: src/program.c:1746 msgid "Find text in toolkits" msgstr "Malkovich" #: src/program.c:1751 msgid "Find text again" msgstr "Malkovich" #: src/program.c:1755 msgid "_Jump To Definition" msgstr "Malkovich" #: src/program.c:1756 msgid "Jump to definition" msgstr "Malkovich" #: src/program.c:1760 msgid "_Info" msgstr "Malkovich" #: src/program.c:1761 msgid "Info on selected object" msgstr "Malkovich" #: src/program.c:1765 msgid "_Trace" msgstr "Malkovich" #: src/program.c:1766 msgid "Make a new trace window" msgstr "Malkovich" #: src/program.c:1770 msgid "_Errors" msgstr "Malkovich" #: src/program.c:1771 msgid "Show all errors" msgstr "Malkovich" #: src/program.c:1785 msgid "Help on _Tool" msgstr "Malkovich" #: src/program.c:1786 msgid "View docs for this tool" msgstr "Malkovich" #: src/program.c:2028 msgid "Bad drag." msgstr "Malkovich" #: src/program.c:2030 msgid "" "Sorry, you can only drag tools between toolkits. You can't reorder toolkits, " "you can't nest toolkits and you can't drag tools to the top level." msgstr "Malkovich" #: src/program.c:2219 msgid "Tool" msgstr "Malkovich" #: src/itext.c:67 msgid "Formula" msgstr "Malkovich" #: src/itext.c:184 src/itext.c:325 msgid "no value" msgstr "Malkovich" #: src/itext.c:503 src/itext.c:536 msgid "Dirty value" msgstr "Malkovich" #: src/model.c:131 #, fuzzy, c-format msgid "" "Unable to load from file \"%s\". Error log is:\n" "%s" msgstr "Malkovich" #: src/model.c:314 src/model.c:369 src/model.c:388 src/filemodel.c:101 #: src/filemodel.c:131 src/filemodel.c:567 src/filemodel.c:608 #, fuzzy, c-format msgid "_%s() not implemented for class \"%s\"." msgstr "Malkovich" #: src/model.c:419 src/filemodel.c:255 src/filemodel.c:270 msgid "XML library error." msgstr "Malkovich" #: src/model.c:420 msgid "model_save: xmlNewChild() failed" msgstr "Malkovich" #: src/model.c:477 msgid "XML load error." msgstr "Malkovich" #: src/model.c:478 #, fuzzy, c-format msgid "Can't load node of type \"%s\" into object of type \"%s\"" msgstr "Malkovich" #: src/model.c:693 msgid "Delete?" msgstr "Malkovich" #: src/model.c:694 #, fuzzy, c-format msgid "Are you sure you want to delete %s \"%s\"?" msgstr "Malkovich" #: src/panechild.c:108 #, fuzzy msgid "Close the pane" msgstr "Malkovich" #: src/imageinfo.c:679 #, fuzzy msgid "User cancelled operation" msgstr "Malkovich" #: src/imageinfo.c:979 msgid "Unable to open image." msgstr "Malkovich" #: src/imageinfo.c:980 #, fuzzy, c-format msgid "Unable to open file \"%s\" as image." msgstr "Malkovich" #: src/imageinfo.c:1110 src/imageinfo.c:1118 msgid "Unable to write to file." msgstr "Malkovich" #: src/imageinfo.c:1111 #, fuzzy, c-format msgid "File \"%s\" is already open for read." msgstr "Malkovich" #: src/imageinfo.c:1119 #, fuzzy, c-format msgid "Error writing image to file \"%s\"." msgstr "Malkovich" #: src/imageinfo.c:1135 msgid "Unable to paint on image." msgstr "Malkovich" #: src/imageinfo.c:1136 #, fuzzy, c-format msgid "" "Unable to get write permission for file \"%s\".\n" "Check permission settings." msgstr "Malkovich" #: src/imageinfo.c:1180 msgid "Modify" msgstr "Malkovich" #: src/imageinfo.c:1181 msgid "Modify disc file?" msgstr "Malkovich" #: src/imageinfo.c:1182 #, fuzzy, c-format msgid "" "This image is being shown directly from the disc file:\n" "\n" " %s\n" "\n" "If you paint on this file, it will be permanently changed. If something goes " "wrong, you may lose work. Are you sure you want to modify this file?" msgstr "Malkovich" #: src/imageinfo.c:1883 msgid "Unable to paint text." msgstr "Malkovich" #: src/imageinfo.c:1884 #, fuzzy, c-format msgid "Unable to paint text \"%s\" in font \"%s\"." msgstr "Malkovich" #: src/row.c:287 msgid "Error in row." msgstr "Malkovich" #. Elements are name of row, principal error, #. * secondary error. #. #: src/row.c:291 #, fuzzy, c-format msgid "" "Error in row %s: %s\n" "%s" msgstr "Malkovich" #. Expands to eg. "B1 refers to: B2, B3". #. #: src/row.c:496 msgid "refers to" msgstr "Malkovich" #. Expands to eg. "B1 is referred to by: B2, B3". #. #: src/row.c:507 msgid "is referred to by" msgstr "Malkovich" #: src/row.c:520 msgid "is blocked on" msgstr "Malkovich" #: src/error.c:60 #, fuzzy msgid "No ierrors found." msgstr "Malkovich" #: src/error.c:100 msgid "No unresolved symbols found." msgstr "Malkovich" #: src/error.c:124 #, fuzzy msgid "Clear ierror window" msgstr "Malkovich" #: src/error.c:128 #, fuzzy msgid "List _iErrors" msgstr "Malkovich" #: src/error.c:129 #, fuzzy msgid "Search for all ierrors" msgstr "Malkovich" #: src/error.c:133 msgid "List _Unresolved" msgstr "" #: src/error.c:134 msgid "Search for all unresolved references" msgstr "" #: src/error.c:139 #, fuzzy msgid "Close ierror window" msgstr "Malkovich" #: src/error.c:222 #, fuzzy, c-format msgid "iError - %s" msgstr "Malkovich" #: src/workspace.c:156 msgid "No objects selected." msgstr "Malkovich" #: src/workspace.c:157 src/workspace.c:162 msgid "Select exactly one object and try again." msgstr "Malkovich" #: src/workspace.c:161 msgid "More than one object selected." msgstr "Malkovich" #: src/workspace.c:493 #, fuzzy msgid "File does not exist." msgstr "Malkovich" #: src/workspace.c:494 #, fuzzy, c-format msgid "File \"%s\" cannot be read." msgstr "Malkovich" #: src/workspace.c:776 src/workspace.c:784 msgid "No backup workspaces found." msgstr "Malkovich" #: src/workspace.c:778 msgid "" "You need to enable \"Auto workspace save\" in Preferences before automatic " "recovery works." msgstr "Malkovich" #: src/workspace.c:785 #, fuzzy, c-format msgid "No suitable workspace save files found in \"%s\"" msgstr "Malkovich" #: src/workspace.c:801 msgid "Open workspace backup?" msgstr "Malkovich" #: src/workspace.c:802 #, fuzzy, c-format msgid "" "Found workspace \"%s\", dated %s. Do you want to recover this workspace?" msgstr "Malkovich" #: src/workspace.c:1220 msgid "Version mismatch." msgstr "Malkovich" #: src/workspace.c:1221 #, fuzzy, c-format msgid "" "File \"%s\" was saved from %s-%d.%d.%d. You may see compatibility problems." msgstr "Malkovich" #: src/workspace.c:1378 #, fuzzy msgid "// private definitions for this workspace\n" msgstr "Malkovich" #: src/workspace.c:1422 src/workspacegroup.c:145 src/tool.c:869 #: src/column.c:326 msgid "Name clash." msgstr "Malkovich" #: src/workspace.c:1423 #, fuzzy, c-format msgid "Can't create workspace \"%s\". A symbol with that name already exists." msgstr "Malkovich" #: src/workspace.c:1514 msgid "Default empty workspace" msgstr "Malkovich" #: src/workspace.c:1586 src/class.c:795 msgid "Wrong number of arguments." msgstr "Malkovich" #: src/workspace.c:1587 #, fuzzy, c-format msgid "%s needs %d arguments, there are %d selected." msgstr "Malkovich" #: src/workspace.c:1598 msgid "Too many names selected." msgstr "Malkovich" #: src/workspace.c:1738 msgid "You can only remove top level rows." msgstr "Malkovich" #: src/workspace.c:1739 msgid "Not all selected objects are top level rows." msgstr "Malkovich" #: src/workspace.c:1789 msgid "Delete selected objects?" msgstr "Malkovich" #: src/workspace.c:1790 #, fuzzy, c-format msgid "Are you sure you want to delete %s?" msgstr "Malkovich" #: src/workspace.c:1848 msgid "Unable to ungroup." msgstr "Malkovich" #: src/workspace.c:1849 #, fuzzy, c-format msgid "Row \"%s\" is not a Group or a list." msgstr "Malkovich" #: src/iimage.c:115 msgid "Original filename" msgstr "Malkovich" #: src/iimage.c:338 msgid "Save timer." msgstr "Malkovich" #: src/iimage.c:339 #, fuzzy, c-format msgid "Image save took %g seconds." msgstr "Malkovich" #: src/tslider.c:369 msgid "Slider value ... edit!" msgstr "Malkovich" #: src/tslider.c:390 msgid "Left-drag to set number" msgstr "Malkovich" #: src/path.c:279 #, fuzzy, c-format msgid "File \"%s\" not found." msgstr "Malkovich" #: src/path.c:306 #, fuzzy, c-format msgid "File \"%s\" not found on path" msgstr "Malkovich" #: src/boxes.c:252 msgid "Close _without Saving" msgstr "Malkovich" #. Translators: translate this to a credit for you, and it'll appear in #. * the About box. #. #: src/boxes.c:271 msgid "translator_credits" msgstr "Malkovich" #: src/boxes.c:280 #, fuzzy, c-format msgid "About %s." msgstr "Malkovich" #: src/boxes.c:283 #, fuzzy, c-format msgid "%s is an image processing package." msgstr "Malkovich" #: src/boxes.c:287 #, fuzzy, c-format msgid "" "%s comes with ABSOLUTELY NO WARRANTY. This is free software and you are " "welcome to redistribute it under certain conditions, see http://www.gnu.org." msgstr "Malkovich" #: src/boxes.c:306 #, fuzzy msgid "Personal start folder" msgstr "Malkovich" #: src/boxes.c:310 #, fuzzy msgid "Homepage" msgstr "Malkovich" #: src/boxes.c:313 #, fuzzy msgid "Linked to VIPS" msgstr "Malkovich" #: src/boxes.c:316 #, fuzzy msgid "Built against VIPS" msgstr "Malkovich" #: src/boxes.c:329 #, fuzzy msgid "Temp files in" msgstr "Malkovich" #: src/boxes.c:395 msgid "Help page not found." msgstr "Malkovich" #: src/boxes.c:396 #, fuzzy, c-format msgid "No indexed help page found for tag \"%s\"" msgstr "Malkovich" #: src/boxes.c:605 msgid "Search for" msgstr "Malkovich" #: src/boxes.c:606 msgid "Case sensitive" msgstr "Malkovich" #: src/boxes.c:608 msgid "Regular expression" msgstr "Malkovich" #: src/boxes.c:610 msgid "Search from start" msgstr "Malkovich" #: src/boxes.c:810 msgid "Image header fields" msgstr "Malkovich" #: src/boxes.c:826 msgid "Image history" msgstr "Malkovich" #: src/boxes.c:942 src/boxes.c:953 src/boxes.c:982 #, fuzzy msgid "Unable to view help file." msgstr "Malkovich" #: src/boxes.c:943 #, fuzzy, c-format msgid "Unable to open URL \"%s\", windows error code = %d." msgstr "Malkovich" #: src/boxes.c:954 #, c-format msgid "" "Attempt to view URL with xdg-open failed\n" "%s" msgstr "" #: src/boxes.c:959 src/boxes.c:991 msgid "Browser window opened." msgstr "Malkovich" #: src/boxes.c:961 src/boxes.c:993 #, fuzzy msgid "You may need to switch desktops to see the new window." msgstr "Malkovich" #: src/boxes.c:984 #, fuzzy, c-format msgid "" "Attempted to launch browser with command:\n" " %s\n" "You can change this command in Preferences." msgstr "Malkovich" #: src/boxes.c:1084 #, fuzzy, c-format msgid "Version %s" msgstr "Malkovich" #: src/boxes.c:1097 #, fuzzy msgid "Starting ..." msgstr "Malkovich" #: src/boxes.c:1105 msgid "Display this splash screen during startup" msgstr "Malkovich" #: src/boxes.c:1174 msgid "Select Font" msgstr "Malkovich" #: src/boxes.c:1232 msgid "Font not found." msgstr "Malkovich" #: src/boxes.c:1233 #, fuzzy, c-format msgid "Font \"%s\" not found on system." msgstr "Malkovich" #: src/boxes.c:1313 msgid "Pick a font" msgstr "Malkovich" #: src/boxes.c:1318 msgid "Set Font" msgstr "Malkovich" #: src/boxes.c:1364 msgid "Click to select font" msgstr "Malkovich" #: src/spin.c:223 msgid "Expand or collapse row" msgstr "Malkovich" #: src/filemodel.c:256 msgid "model_save_filename: xmlNewDoc() failed" msgstr "Malkovich" #: src/filemodel.c:271 msgid "model_save_filename: xmlNewDocNode() failed" msgstr "Malkovich" #: src/filemodel.c:287 msgid "Save failed." msgstr "Malkovich" #: src/filemodel.c:288 #, fuzzy, c-format msgid "" "Save of %s \"%s\" to file \"%s\" failed.\n" "%s" msgstr "Malkovich" #: src/filemodel.c:343 msgid "filemodel_real_save_all: no save method" msgstr "Malkovich" #: src/filemodel.c:453 #, fuzzy, c-format msgid "Can't load XML file \"%s\", it's not a %s save file." msgstr "Malkovich" #: src/filemodel.c:462 #, fuzzy, c-format msgid "" "Can't load XML file \"%s\", unable to extract version information from " "namespace." msgstr "Malkovich" #: src/filemodel.c:475 #, fuzzy, c-format msgid "Can't load XML file \"%s\", the file does not contain a %s." msgstr "Malkovich" #. Expands to (eg.) "Save Column A2". #. #: src/filemodel.c:660 #, fuzzy, c-format msgid "Save %s %s" msgstr "Malkovich" #: src/filemodel.c:736 src/filemodel.c:748 msgid "Object has been modified." msgstr "Malkovich" #: src/filemodel.c:737 #, fuzzy, c-format msgid "" "%s \"%s\" has been modified since you loaded it from file \"%s\".\n" "\n" "Do you want to save your changes?" msgstr "Malkovich" #: src/filemodel.c:749 #, fuzzy, c-format msgid "%s \"%s\" has been modified. Do you want to save your changes?" msgstr "Malkovich" #: src/workspacegroup.c:146 #, fuzzy, c-format msgid "" "Can't create workspacegroup \"%s\". A symbol with that name already exists." msgstr "Malkovich" #: src/workspacegroup.c:213 msgid "Set workspace name here" msgstr "Malkovich" #: src/workspacegroup.c:215 msgid "Set workspace caption here" msgstr "Malkovich" #: src/workspacegroup.c:217 msgid "New Workspace" msgstr "Malkovich" #: src/workspacegroup.c:221 msgid "Create Workspace" msgstr "Malkovich" #: src/matrix.c:148 msgid "Sliders" msgstr "Malkovich" #: src/matrix.c:149 msgid "Toggle buttons" msgstr "Malkovich" #: src/matrix.c:150 msgid "Text, plus scale and offset" msgstr "Malkovich" #: src/matrix.c:161 msgid "Display as" msgstr "Malkovich" #: src/matrix.c:192 #, fuzzy, c-format msgid "Edit Matrix \"%s\"" msgstr "Malkovich" #: src/matrix.c:197 msgid "Set Matrix" msgstr "Malkovich" #: src/matrix.c:272 msgid "Display" msgstr "Malkovich" #: src/columnview.c:133 #, fuzzy, c-format msgid "Save Column \"%s\"" msgstr "Malkovich" #: src/columnview.c:181 src/columnview.c:243 msgid "Toolkit" msgstr "Malkovich" #: src/columnview.c:241 msgid "Set menu item text here" msgstr "Malkovich" #: src/columnview.c:243 msgid "Add to this toolkit" msgstr "Malkovich" #: src/columnview.c:245 msgid "Store column in this file" msgstr "Malkovich" #: src/columnview.c:248 #, fuzzy, c-format msgid "New Menu Item from Column \"%s\"" msgstr "Malkovich" #: src/columnview.c:253 msgid "Menuize" msgstr "Malkovich" #: src/columnview.c:648 msgid "Edit caption, press enter to accept changes, press escape to cancel" msgstr "Malkovich" #: src/columnview.c:701 msgid "Enter expressions here" msgstr "Malkovich" #: src/columnview.c:755 msgid "doubleclick to set title" msgstr "" #: src/columnview.c:764 msgid "Fold the column away" msgstr "Malkovich" #: src/columnview.c:769 msgid "Open the column" msgstr "Malkovich" #: src/columnview.c:893 msgid "Column menu" msgstr "Malkovich" #: src/columnview.c:894 msgid "_Edit Caption" msgstr "Malkovich" #: src/columnview.c:903 msgid "Make Column Into _Menu Item" msgstr "Malkovich" #: src/columnview.c:940 msgid "Left-drag to move, left-double-click to set title, right-click for menu" msgstr "Malkovich" #: src/columnview.c:973 msgid "Delete the column" msgstr "Malkovich" #. used as in "fred refers to undefined symbol jim" #. #: src/tool.c:77 msgid "refers to undefined symbol" msgstr "Malkovich" #: src/tool.c:870 #, fuzzy, c-format msgid "" "Can't create dialog with name \"%s\", an object with that name already " "exists in kit \"%s\"." msgstr "Malkovich" #: src/compile.c:1449 msgid "Too many shared nodes in graph." msgstr "Malkovich" #: src/compile.c:1451 msgid "Raise MAX_RELOC" msgstr "Malkovich" #: src/compile.c:1768 #, fuzzy, c-format msgid "Member \"%s\" of class \"%s\" should have no arguments." msgstr "Malkovich" #: src/compile.c:2645 #, fuzzy msgid "pattern match failed" msgstr "Malkovich" #: src/idialog.c:432 msgid "Pin up" msgstr "Malkovich" #: src/idialog.c:434 msgid "Check this to pin the dialog up" msgstr "Malkovich" #: src/link.c:578 msgid "Circular dependency." msgstr "Malkovich" #: src/link.c:579 #, fuzzy, c-format msgid "Circular dependency detected near symbol \"%s\"." msgstr "Malkovich" #: src/colourdisplay.c:259 msgid "Double-click to edit this color, or drag-and-drop between colors" msgstr "Malkovich" #: src/toolkitgroup.c:139 #, fuzzy, c-format msgid "Toolkits for %s" msgstr "Malkovich" #: src/dump.c:187 msgid "parameter" msgstr "Malkovich" #: src/dump.c:188 msgid "zombie" msgstr "Malkovich" #: src/dump.c:190 msgid "workspace group" msgstr "Malkovich" #: src/dump.c:191 msgid "root symbol" msgstr "Malkovich" #: src/dump.c:192 msgid "external symbol" msgstr "Malkovich" #: src/dump.c:193 msgid "built-in symbol" msgstr "Malkovich" #: src/column.c:327 #, fuzzy, c-format msgid "Can't create column \"%s\". A column with that name already exists." msgstr "Malkovich" #: src/column.c:364 msgid "Empty column." msgstr "Malkovich" #: src/column.c:365 msgid "There are no objects in the current column." msgstr "Malkovich" #: src/column.c:384 msgid "Too few items." msgstr "Malkovich" #: src/column.c:385 #, fuzzy, c-format msgid "This column only has %d items, but %s needs %d items." msgstr "Malkovich" #: src/class.c:50 #, fuzzy, c-format msgid "Object %s is not a class." msgstr "Malkovich" #: src/class.c:333 msgid "No such secret." msgstr "Malkovich" #: src/class.c:334 msgid "" "Editing local classes which reference non-local objects is a bit broken at " "the moment :-(" msgstr "Malkovich" #: src/class.c:658 #, fuzzy, c-format msgid "You can't have more than %d arguments to a superclass constructor." msgstr "Malkovich" #: src/class.c:787 src/class.c:846 msgid "Bad superclass." msgstr "Malkovich" #: src/class.c:788 #, fuzzy, c-format msgid "Superclass constructor \"%s\" should have no secret arguments." msgstr "Malkovich" #: src/class.c:796 #, fuzzy, c-format msgid "Superclass constructor \"%s\" expects %d arguments, not %d." msgstr "Malkovich" #: src/class.c:850 #, fuzzy, c-format msgid "First element in superclass of \"%s\" must be class or constructor." msgstr "Malkovich" #: src/class.c:882 src/class.c:979 #, fuzzy, c-format msgid "" "Too many arguments to class constructor \"%s\". No more than %d arguments " "are supported." msgstr "Malkovich" #: src/class.c:972 msgid "Class not found." msgstr "Malkovich" #: src/class.c:973 #, fuzzy, c-format msgid "Class \"%s\" not found." msgstr "Malkovich" #: src/class.c:1010 #, fuzzy, c-format msgid "Member \"%s\" of class \"%s\" should be of type \"%s\", instead it's:" msgstr "Malkovich" #: src/plot.c:80 msgid "YYYY" msgstr "" #: src/plot.c:81 msgid "XYYY" msgstr "" #: src/plot.c:82 msgid "XYXY" msgstr "" #: src/plot.c:93 #, fuzzy msgid "Point" msgstr "Malkovich" #: src/plot.c:95 #, fuzzy msgid "Spline" msgstr "Malkovich" #: src/plot.c:96 msgid "Bar" msgstr "" #: src/plot.c:141 msgid "More than one column needed or XY plots" msgstr "" #: src/plot.c:151 msgid "Even number of columns only for XY format plots" msgstr "" #: src/plot.c:316 #, fuzzy msgid "Format" msgstr "Malkovich" #: src/plot.c:319 #, fuzzy msgid "Style" msgstr "Malkovich" #: src/plot.c:322 msgid "Xmin" msgstr "" #: src/plot.c:325 msgid "Xmax" msgstr "" #: src/plot.c:328 msgid "Ymin" msgstr "" #: src/plot.c:331 msgid "Ymax" msgstr "" #: src/plot.c:337 msgid "Options" msgstr "Malkovich" #: src/plot.c:361 msgid "1xn or nx1 images only for Plot" msgstr "" #: src/plot.c:380 #, fuzzy msgid "Unable to prepare image." msgstr "Malkovich" #: src/plot.c:393 msgid "1xn or nx1 images only" msgstr "" #: src/rowview.c:304 msgid "You can only duplicate top level rows." msgstr "Malkovich" #: src/rowview.c:457 msgid "Drag between columns not yet implemented." msgstr "Malkovich" #. Other init. #. #: src/rowview.c:585 msgid "Row menu" msgstr "Malkovich" #: src/rowview.c:598 msgid "Re_set" msgstr "Malkovich" #: src/rowview.c:664 msgid "Click to open or close class" msgstr "Malkovich" #: src/iregion.c:166 #, fuzzy, c-format msgid "at (%d, %d), size (%d, %d)" msgstr "Malkovich" #: src/iregion.c:181 src/iregion.c:223 msgid "Left" msgstr "Malkovich" #: src/iregion.c:182 src/iregion.c:226 msgid "Top" msgstr "Malkovich" #: src/iregion.c:183 src/iregion.c:229 msgid "Width" msgstr "Malkovich" #: src/iregion.c:184 src/iregion.c:232 msgid "Height" msgstr "Malkovich" #: src/iregion.c:223 msgid "Left edge of region" msgstr "Malkovich" #: src/iregion.c:226 msgid "Top edge of region" msgstr "Malkovich" #: src/iregion.c:229 msgid "Width of region" msgstr "Malkovich" #: src/iregion.c:232 msgid "Height of region" msgstr "Malkovich" #: src/iregion.c:237 #, fuzzy, c-format msgid "Edit \"%s\"" msgstr "Malkovich" #: src/iregion.c:241 msgid "Set Region" msgstr "Malkovich" #~ msgid "No match found." #~ msgstr "Malkovich" #~ msgid "Thumbnails" #~ msgstr "Malkovich" #, fuzzy #~ msgid "Image files found in: \"%s\"" #~ msgstr "Malkovich" #~ msgid "Searching ..." #~ msgstr "Malkovich" #~ msgid "Search incomplete!" #~ msgstr "Malkovich" #~ msgid "Show thumbnails" #~ msgstr "Malkovich" #~ msgid "Show thumbnails for files in this directory" #~ msgstr "Malkovich" #~ msgid "Stack overflow." #~ msgstr "Malkovich" #~ msgid "Spine stack overflow, runaway recursion?" #~ msgstr "Malkovich" #~ msgid "Frame stack overflow, expression too complex." #~ msgstr "Malkovich" #~ msgid "Stack underflow." #~ msgstr "Malkovich" #~ msgid "Frame stack underflow, you've found a bug!" #~ msgstr "Malkovich" #~ msgid "Spine stack underflow, you've found a bug!" #~ msgstr "Malkovich" #~ msgid "No options." #~ msgstr "Malkovich" #~ msgid "You need at least one option in your option list" #~ msgstr "Malkovich" #~ msgid "Set option caption here" #~ msgstr "Malkovich" #~ msgid "Set option default value here" #~ msgstr "Malkovich" #~ msgid "Edit Option" #~ msgstr "Malkovich" #~ msgid "Set Option" #~ msgstr "Malkovich" #~ msgid "Orderlist menu" #~ msgstr "Malkovich" #~ msgid "Delete _Selected" #~ msgstr "Malkovich" #~ msgid "Delete _All" #~ msgstr "Malkovich" #~ msgid "Current options - right button for menu" #~ msgstr "Malkovich" #~ msgid "Enter new option fields here" #~ msgstr "Malkovich" #, fuzzy #~ msgid "Zoom to fit plot to window" #~ msgstr "Malkovich" #, fuzzy #~ msgid "Bad regular expression \"%s\"." #~ msgstr "Malkovich" #, fuzzy #~ msgid "Loading toolkit \"%s\"" #~ msgstr "Malkovich" #, fuzzy #~ msgid "Loading workspace \"%s\"" #~ msgstr "Malkovich" #~ msgid "Loading plugins" #~ msgstr "Malkovich" #~ msgid "First recalculation ..." #~ msgstr "Malkovich" #~ msgid "Final recalculation ..." #~ msgstr "Malkovich" #~ msgid "Building main window" #~ msgstr "Malkovich" #~ msgid "Initialising toolkit" #~ msgstr "Malkovich" #~ msgid "GObject is not a VipsObject." #~ msgstr "Malkovich" #~ msgid "VIPS8 error." #~ msgstr "Malkovich" #~ msgid "VIPS8 not linked." #~ msgstr "Malkovich" #, fuzzy #~ msgid "static string \"%s\"" #~ msgstr "Malkovich" #~ msgid "Re_cover After Crash" #~ msgstr "Malkovich" #~ msgid "VIPS image files (*.v)" #~ msgstr "Malkovich" #, fuzzy #~ msgid "Unable to open \"%s\" for write." #~ msgstr "Malkovich" #~ msgid "Right hand side of '.' is not tag." #~ msgstr "Malkovich" #~ msgid "Unable to open location." #~ msgstr "Malkovich" #~ msgid "" #~ "Opened window for URL:\n" #~ " %s\n" #~ "This may take a few seconds." #~ msgstr "Malkovich" #~ msgid "Computing image." #~ msgstr "Malkovich" #~ msgid "" #~ "Calculating all the pixels in an image. Press the Cancel button to stop " #~ "computation early." #~ msgstr "Malkovich" #~ msgid "TIFF image files (*.tif, *.tiff)" #~ msgstr "Malkovich" #~ msgid "JPEG image files (*.jpg, *.jpeg, *.jpe)" #~ msgstr "Malkovich" #~ msgid "PNG image files (*.png)" #~ msgstr "Malkovich" #~ msgid "PPM image files (*.ppm, *.pgm, *.pbm)" #~ msgstr "Malkovich" #~ msgid "OpenEXR image files (*.exr)" #~ msgstr "Malkovich" #~ msgid "Analyze image files (*.img, *.hdr)" #~ msgstr "Malkovich" #~ msgid "CSV files (*.csv)" #~ msgstr "Malkovich" #~ msgid "from" #~ msgstr "Malkovich" #~ msgid "No group" #~ msgstr "Malkovich" #~ msgid "%s version: %s" #~ msgstr "Malkovich" #~ msgid "Calculating ..." #~ msgstr "Malkovich" #~ msgid "" #~ "Unable to load from file \"%s\". Error loading as image, workspace or " #~ "matrix." #~ msgstr "Malkovich" #~ msgid "Edit _Toolkits" #~ msgstr "Malkovich" #~ msgid "" #~ "unable to compile \"%s\"\n" #~ "%s" #~ msgstr "Malkovich" #~ msgid "Program" #~ msgstr "Malkovich" #~ msgid "Link report." #~ msgstr "Malkovich" #~ msgid "Error report." #~ msgstr "Malkovich" #~ msgid "_Link" #~ msgstr "Malkovich" #~ msgid "Show a link report" #~ msgstr "Malkovich" #~ msgid "defined in toolkit \"%s\"" #~ msgstr "Malkovich" #~ msgid "line %d" #~ msgstr "Malkovich" #~ msgid "tool \"%s\"" #~ msgstr "Malkovich" #~ msgid "toolkit \"%s\"" #~ msgstr "Malkovich" #~ msgid "TIFF image" #~ msgstr "Malkovich" #~ msgid "JPEG image" #~ msgstr "Malkovich" #~ msgid "PNG image" #~ msgstr "Malkovich" #~ msgid "PPM/PGM/PBM image" #~ msgstr "Malkovich" #~ msgid "OpenEXR image" #~ msgstr "Malkovich" #~ msgid "Analyze image" #~ msgstr "Malkovich" #~ msgid "libMagick-supported image" #~ msgstr "Malkovich" #~ msgid "VIPS image" #~ msgstr "Malkovich" ================================================ FILE: proj/README ================================================ sample config and makefiles for building with the MSC toolchain experts only, no support! ================================================ FILE: proj/src/makefile.msc ================================================ # autogenerated from automake.am with automake.py TOP = ..\.. PRJ_TOP = .. PACKAGE = nip PKG_VER = 7.8.6 !INCLUDE $(TOP)\glib\build\win32\make.msc !IFDEF DEBUG LDFLAGS = $(LDFLAGS) /NODEFAULTLIB:msvcrt !ENDIF top_srcdir = $(PRJ_TOP) top_builddir = $(PRJ_TOP) includedir = $(PRJ_TOP) LT_RELEASE = $(PKG_VER) SUBDIRS = # BITMAPS sub-all: for %d in ($(SUBDIRS)) do nmake -nologo -f makefile.msc sub-one THIS=%d sub-one: cd $(THIS) nmake -nologo -f makefile.msc cd .. YACC = \ @YACC@ -d OBJECTS = \ action.obj \ asynch.obj \ boxes.obj \ browse.obj \ builtin.obj \ class.obj \ colourdisplay.obj \ column.obj \ columnview.obj \ command.obj \ compile.obj \ conversion.obj \ conversionview.obj \ doubleclick.obj \ dump.obj \ expr.obj \ filemodel.obj \ filename.obj \ filenameview.obj \ filesel.obj \ graph.obj \ gtkfilesel2.obj \ gtkmenubar2.obj \ gtkutil.obj \ heap.obj \ heapmodel.obj \ classmodel.obj \ idialog.obj \ iimage.obj \ iimageview.obj \ iregionview.obj \ iregiongroup.obj \ iregiongroupview.obj \ regionview.obj \ iregion.obj \ iarrow.obj \ iarrowview.obj \ imageinfo.obj \ imagedisplay.obj \ imagepresent.obj \ imageview.obj \ iwindow.obj \ led.obj \ link.obj \ main.obj \ mainw.obj \ matrix.obj \ matrixview.obj \ model.obj \ orderitem.obj \ orderlist.obj \ paintboxview.obj \ path.obj \ predicate.obj \ watch.obj \ reduce.obj \ rhs.obj \ rhsview.obj \ row.obj \ rowview.obj \ secret.obj \ slider.obj \ colour.obj \ real.obj \ realview.obj \ option.obj \ tslider.obj \ optionview.obj \ sliderview.obj \ colourview.obj \ spin.obj \ statusview.obj \ subcolumn.obj \ subcolumnview.obj \ symbol.obj \ table.obj \ itext.obj \ itextview.obj \ toggle.obj \ toggleview.obj \ tool.obj \ toolkit.obj \ toolkitgroup.obj \ toolkitgroupview.obj \ toolkitview.obj \ toolview.obj \ trace.obj \ program.obj \ tree.obj \ util.obj \ view.obj \ vips_call.obj \ wild.obj \ workspace.obj \ workspacegroup.obj \ workspacegroupview.obj \ workspaceview.obj \ \ lex.obj \ parse.obj \ ADDONS = \ \ regex.obj \ fnmatch.obj regex.obj : regex.c regex.h $(CC) $(CFLAGS) -GD -c -DREGEX_MALLOC -DHAVE_STRING_H $(PKG_CFLAGS) regex.c $(PACKAGE).res : $(PACKAGE).rc $(PACKAGE).ico rc -r -fo $(PACKAGE).res $(PACKAGE).rc RESOURCE = $(PACKAGE).res EXTRA_DIST = \ doc LDADD = \ @IP_CFLAGS@ @IP_LIBS@ CLEANFILES = \ parse.c \ parse.h \ lex.c \ tags INCLUDES = \ -FI msvc_recommended_pragmas.h \ -DIM_NO_VIPS7_COMPAT \ -I $(PRJ_TOP) -DHAVE_CONFIG_H -I. \ $(VIPS_CFLAGS) \ $(GTK_CFLAGS) -DGTK_ENABLE_BROKEN \ $(LIBXML2_CFLAGS) \ $(DIRENT_CFLAGS) PKG_LINK = \ $(VIPS_LIBS) \ $(GLIB_LIBS) $(GTK_LIBS) \ $(DIRENT_LIBS) \ $(LIBXML2_LIBS) all : \ $(PRJ_TOP)\config.h \ sub-all \ $(PACKAGE).exe # bison --defines --output=parse d:\devel\my-gtk\nip-7.8.6\src\parse.y lex.c : lex.l flex -olex.c lex.l $(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h RESOURCE = $(PACKAGE).res $(PACKAGE).lib : $(OBJECTS) lib /out:$(PACKAGE).lib $(OBJECTS) $(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def $(PACKAGE).exe : $(OBJECTS) $(PACKAGE).res $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib \ $(LDFLAGS) .c.obj : $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< ================================================ FILE: proj/src/nip.rc ================================================ nip ICON "nip.ico" ================================================ FILE: share/Makefile.am ================================================ SUBDIRS = nip2 ================================================ FILE: share/nip2/Makefile.am ================================================ SUBDIRS = rc data start compat ================================================ FILE: share/nip2/compat/7.10/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; _L = default_value?0; _a = default_value?1; _b = default_value?2; lightness = Slider 0 100 _L; ab_slice = Image (lab_slice size lightness.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [lightness.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } } #separator Colour_convert_item = class Menupullright (_ "_Convert To") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Convert to") (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Tag as") (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Colour Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum Whitepoints (_ "Old whitepoint") "D65"; new_white = Option_enum Whitepoints (_ "New whitepoint") "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum Render_intent.names (_ "Render intent") (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; green_red = Slider (-20) 20 0; blue_yellow = Slider (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, green_red.value, blue_yellow.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; hue = Slider 0 360 0; saturation = Slider 0.01 5 1; brightness = Slider 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [bv, sv, 1] + Vector [0, 0, hv]; bv = brightness.value; sv = saturation.value; hv = hue.value; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; dE_threshold = Slider 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < dE_threshold { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_image_to_colour_item = class Menuaction (_ "Im_age to Colour") (_ "convert image to colour") { action x = map_unary test x { test x = to_colour x, is_Image x = to_colour (Image pixel), is_Mark x = error (_ "Colour_from_image: arg not Image or Mark: " ++ print x) { pixel = extract_area x.left x.top 1 1 x; } } } Colour_colour_to_image_item = class Menuaction (_ "C_olour to _Image") (_ "convert colour to image") { action x = class _result { _vislevel = 3; width = Expression (_ "Width of image to make") 64; height = Expression (_ "Height of image to make") 64; _result = map_unary (Image @ build) x { build in = image_new (to_real width) (to_real height) 3 Image_format.FLOAT Image_coding.NOCODING (get_type in) in 0 0; } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; _result = map_unary chart x { chart in = measure 0 0 in.width in.height (to_real pacross) (to_real pdown) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make S_ynthetic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.10/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; smooth_threshold = Slider 0 5 1.5; brighten_max = Slider 1 50 10; darken_max = Slider 1 50 50; flat_sharp = Slider (-2) 5 1; jaggy_sharp = Slider (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size smooth_threshold brighten_max darken_max flat_sharp jaggy_sharp in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur" "blur with tuneable parameters" { action x = class _result { _vislevel = 3; radius = Slider 1 50 1; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; _result = map_unary process x { process in = convsep mask in { mask = matrix_blur radius.value, shape.value == 0 = matrix_gaussian_blur radius.value; } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; _result = map_unary process x { process in = Image in' { conv_fn = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_convf, !separable && type == 1 = im_convsepf, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 5) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) ((guess_size + 1) / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; width = Expression "Window width" 3; height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real width * to_real height + 1) / 2)); _result = map_unary process x { process in = rank width height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; threshold = Slider 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more threshold.value) x; } } sep1 = Menuseparator; Dilate8_item = class Menuaction "Dilate _8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "Dilate _4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } Erode8_item = class Menuaction "_Erode 8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "E_rode 4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value frequency_cutoff.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) frequency_cutoff.value ring_width.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) frequency_cutoff.value amplitude_cutoff.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; order = Slider 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) order.value frequency_cutoff.value amplitude_cutoff.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; order = Slider 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; order = Slider 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; offset = Slider (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + offset; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Slider 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Slider 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; width = Expression "Window width" 20; height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local width.expr height.expr in; } } } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Slider (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Slider (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Slider (-1) 1 0; shift = Slider (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Slider (-1) 1 0; shift = Slider (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Slider (-1) 1 0; hshift = Slider (-1) 1 0; vshift = Slider (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Slider_blend_item = class Menuaction "_Slider Blend" "blend two objects together with a slider" { action a b = class _result { _vislevel = 3; blend = Slider 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - blend) + im2 * blend; } } } Image_blend_item = class Menuaction "_Image Blend" "use an image to blend two objects" { action a b c = map_trinary process a b c { process a b c = blend condition in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; args' = sortc compare [a, b, c]; condition = args'?0; in1 = args'?1; in2 = args'?2; } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Slider 0 1 0.5; blend_width = Slider 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Slider 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first args = sortc (const (is_colour_type @ get_type)) [a, b]; mono = args?0; colour = args?1; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Slider 0 (image.bands - 1) 0; display = Option "Display as" [ "Grey", "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider f t v = class scope.Slider f t ((int) v) { Slider = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Slider 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; radius = Slider 0 50 5; highlights = Slider 0 100 95; glow = Slider 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur radius.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; shadow_x = Slider (-50) 50 5; shadow_y = Slider (-50) 50 5; shadow_softness = Slider 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = shadow_softness.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect shadow_x.value shadow_y.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/7.10/Format.def ================================================ Format_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Format_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = Group x; } Format_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = x.value; } #separator Format_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Format_assemble_item = class Menuaction "_Assemble Objects" "assemble a list (or group) of objects into a single object" { action x = ass x.value, is_Group x = ass x { ass x = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = map_unary ass x, is_list x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.10/Histogram.def ================================================ Hist_new_item = class Menuaction "_New Histogram" "make a new histogram" { action = class _result { _vislevel = 3; depth = Option "Depth" ["8 bit", "16 bit"] 0; black = Slider 0 100 0; white = Slider 0 100 100; shadow_point = Slider 0.1 0.3 0.2; mid_point = Slider 0.4 0.6 0.5; highlight_point = Slider 0.7 0.9 0.8; shadow_adjust = Slider (-15) 15 0; mid_adjust = Slider (-30) 30 0; highlight_adjust = Slider (-15) 15 0; _result = tone_build fmt black white shadow_point mid_point highlight_point shadow_adjust mid_adjust highlight_adjust { fmt = [Image_format.UCHAR, Image_format.USHORT]?depth; } } } Hist_find_item = class Menupullright "_Find Histogram" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } } Hist_map_item = class Menuaction "_Map Histogram" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { args = sortc (const is_hist) [a, b]; im = args?0; hist = args?1; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Cumulativise Histogram" "form cumulative histogram from frequency histogram" { action x = map_unary hist_cum x; } Hist_norm_item = class Menuaction "N_ormalise Histogram" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_match_item = class Menuaction "Ma_tch Histogram" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (image_set_type Image_type.HISTOGRAM @ Image) [ im_profile image.value 0, im_profile image.value 1, im_profile (fliptb image.value) 0, im_profile (fliplr image.value) 1 ]?edge; } } } Hist_graph_item = class Menuaction "_Slice Image" "slice an image along a guide" { action x = map_unary process x { process guide = image_set_type Image_type.HISTOGRAM slice { slice = extract_area 0 guide.top guide.width 1 guide.image, is_HGuide guide = extract_area guide.left 0 1 guide.height guide.image; } } } ================================================ FILE: share/nip2/compat/7.10/Image.def ================================================ Image_new_item = class Menuaction "New _Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum Image_type.type_names "Image type" "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "New _From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "New _Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } } #separator Image_number_format_item = class Menupullright "_Number Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_unary extract x { extract im = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; _sorted = sortc _pred [a, b]; _a' = _sorted?0; _b' = _sorted?1; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_join_item = class Menupullright "_Join" "join images together left/right or up/down" { join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Slider 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Slider 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Slider (-100) (100) 0; vshim = Slider (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list x); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 10; voverlap = Expression "Vertical overlap" 10; _result = map_unary (Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Slider 0.001 3 1; offset = Slider (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Slider 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; shadow_adjust = Slider (-15) 15 0; mid_adjust = Slider (-30) 30 0; highlight_adjust = Slider (-15) 15 0; shadow_point = Slider 0.1 0.3 0.2; mid_point = Slider 0.4 0.6 0.5; highlight_point = Slider 0.7 0.9 0.8; black = Slider 0 100 0; white = Slider 0 100 100; curve = tone_build x.format black white shadow_point mid_point highlight_point shadow_adjust mid_adjust highlight_adjust; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Slider (-180) 180 0; _result = map_unary process x { process image = rotate angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = map_unary straighten x { straighten arrow = rotate angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { _interp = Option_enum Interpolate.names "Interpolation" "Bilinear"; Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = _interp; _result = map_unary process x { process image = resize xfactor yfactor interp.value_thing image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; interp = _interp; _result = map_unary process x { process image = resize fac fac interp.value_thing image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = max_pair xfac yfac; min_factor = min_pair xfac yfac; fac = [max_factor, min_factor, xfac, yfac]?which; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.BILINEAR; rubber_order = Option "order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image scale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _transform { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform _result = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image _result?0; _transform = Transform _result?1 reference.width reference.height; final_error = _result?2; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first args = sortc (const is_Transform) [a, b]; i = args?0; t = args?1; t' = t.scale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; sep2 = Menuseparator; Resize_canvas_item = class Menuaction "Resize _Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_edit_header_item = class Menuaction "Change _Header" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum Image_type.type_names "Image type" (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = ramp, orientation == 0 = rot90 ramp { fn = im_grey, foption == 0 = im_fgrey; ramp = Image (fn (to_real nwidth) (to_real nheight)); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Slider 0 255 128; deviation = Slider 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Slider 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Slider 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value frequency_cutoff.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) frequency_cutoff.value ring_width.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) frequency_cutoff.value amplitude_cutoff.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; order = Slider 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value frequency_cutoff.value amplitude_cutoff.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; order = Slider 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; order = Slider 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Slider 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Slider 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 2; ssize = Expression "Step size" 2; _result = Image (colour_transform_to (get_type start) im) { base = colour_transform_to Image_type.LAB start; step = to_real ssize; offset = to_real nstep * step; range c = [c - offset, c - offset + step ... c + offset]; mk_patch b a = image_new 150 150 3 Image_format.FLOAT Image_coding.NOCODING Image_type.LAB (Vector [base?0, a, b]) 0 0; mk_strip b = map (mk_patch b) (range base?1); mk_grid = map mk_strip (range base?2); im = imagearray_assemble 15 15 mk_grid; } } } } ================================================ FILE: share/nip2/compat/7.10/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.10 start_DATA = \ Colour.def \ _convert.def \ Filter.def \ Format.def \ _generate.def \ Histogram.def \ Image.def \ _joe_extra.def \ _joe_utilities.def \ _list.def \ Math.def \ Matrix.def \ _predicate.def \ _stdenv.def \ Tasks.def \ _types.def \ Widgets.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.10/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_And" "bitwise and of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_Or" "bitwise or of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "E_xclusive Or" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_Not" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary if_then_else a b c; } Band_or_item = class Menuaction "Band O_r" "or the bands of an image together" { action im = map_unary (foldr1 bitwise_or @ bandsplit) im; } Band_and_item = class Menuaction "Band A_nd" "and the bands of an image together" { action im = map_unary (foldr1 bitwise_and @ bandsplit) im; } } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = n ? x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = a ++ b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = a : x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Mean_item = class Menuaction "_Mean" "mean value" { action a = map_unary mean a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Stats_item = class Menuaction "_Many Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "Position of M_aximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "Position of M_inimum" "position of minimum in object" { action a = map_unary minpos a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.10/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; sigma = Slider 0.001 10 1; min_amplitude = Slider 0 1 0.2; integer = Toggle "Integer" false; _result = fn sigma.value min_amplitude.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; sigma = Slider 0.001 10 1.5; min_amplitude = Slider 0 1 0.1; integer = Toggle "Integer" false; _result = fn sigma.value min_amplitude.value { fn = im_log_imask, integer = im_log_dmask; } } } } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { extract n f = take (to_real n) @ drop (to_real f); Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process mat = mat.Matrix_base (extract number first mat.value); } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = mat.Matrix_base (map (extract number first) mat.value); } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 (extract 1) [0..] mat.value), which == 0 = mat.Matrix_base (map2 (extract 1) [mat.width - 1, mat.width - 2 .. 0] mat.value); } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // insert a thing in a list at position first insert first x l = take first l ++ x ++ drop first l; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process mat = mat.Matrix_base (insert (to_real first) rows mat.value) { row = replicate mat.width (to_real item); rows = replicate (to_real number) row; } } } } Columns_item = class Menuaction "_Columns" "remove columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process mat = mat.Matrix_base (map (insert (to_real first) cells) mat.value) { cells = replicate (to_real number) (to_real item); } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process mat = mat.Matrix_base (delete first number mat.value); } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process mat = mat.Matrix_base (map (delete first number) mat.value); } } } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_convert_to_image_item = class Menuaction "Matrix to I_mage" "convert matrix to image" { action x = class _result { _vislevel = 3; conversion = Option "Convert to" [ "Monochrome image, same size as matrix", "Multiband image, each row becomes a pixel" ] 0; _result = map_unary process x { process mat = Image im, conversion == 0 = Image joinup { im = im_mask2vips mat; joinup = bandjoin (map extract_column [0 .. mat.width - 1]); extract_column n = extract_area n 0 1 mat.height im; } } } } Matrix_from_image_item = class Menuaction "Image to M_atrix" "convert image to matrix" { action x = map_unary to_matrix x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix_width 2] ]; _vislevel = 3; gwidth = Expression "Graph size across (pixels)" 512; gheight = Expression "Graph size down (pixels)" 512; plot_colour = Colour_picker "Lab" [80, -80, 80]; xmin = Expression "X range minimum" (foldr1 min_pair (map (extract 0) x.value)); xmax = Expression "X range maximum" (foldr1 max_pair (map (extract 0) x.value)); ymin = Expression "Y range minimum" (foldr1 min_pair (map (extract 1) x.value)); ymax = Expression "Y range maximum" (foldr1 max_pair (map (extract 1) x.value)); axies = Toggle "Draw axies" true; mark = Mark this p?0 p?1 { p = _to_image x.value?0; } mark_hint = "Mark is at position:"; mark_position = _from_image [mark.left, mark.top]; // geometry _xrange = to_real xmax - to_real xmin; _yrange = to_real ymax - to_real ymin; _xscale = to_real gwidth / _xrange; _yscale = to_real gheight / _yrange; // map an [x,y] point into the image coordinates _to_image p = [(p?0 - to_real xmin) * _xscale, to_real gheight - (p?1 - to_real ymin) * _yscale]; // map an [x,y] point from image cods back to real cods _from_image p = [p?0 / _xscale + to_real xmin, (to_real gheight - p?1) / _yscale + to_real ymin]; _result = Image (foldr plot background' x.value) { // colourspace we are drawing in space = Image_type.colour_spaces.lookup 0 1 plot_colour.colour_space; plot_image_new width height pixel = image_new width height 3 Image_format.FLOAT Image_coding.NOCODING space pixel 0 0; // background background = plot_image_new (to_real gwidth) (to_real gheight) 0; // mark we plot mark_width = max_pair 1 (to_real gwidth / 100); mark_height = max_pair 1 (to_real gheight / 100); mark = plot_image_new mark_width mark_height plot_colour; // draw axies on background background' = drawxy, axies = background { // colour we draw axies in ax_col = colour_transform_to space (Colour "Lab" [100, 0, 0]); // axies xaxis = plot_image_new (to_real gwidth) 1 ax_col; yaxis = plot_image_new 1 (to_real gheight) ax_col; origin = _to_image [0, 0]; drawx = insert_noexpand 0 origin?1 xaxis background; drawxy = insert_noexpand origin?0 0 yaxis drawx; } // plot a single point on an image plot p im = insert_noexpand (x - mark_width / 2) (y - mark_height / 2) mark im { p' = _to_image p; x = p'?0; y = p'?1; } } } } ================================================ FILE: share/nip2/compat/7.10/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; brightness = Slider 0 32767 prefs.VIDEO_BRIGHTNESS; colour = Slider 0 32767 prefs.VIDEO_COLOUR; contrast = Slider 0 32767 prefs.VIDEO_CONTRAST; hue = Slider 0 32767 prefs.VIDEO_HUE; frames_hint = "Average this many frames:"; frames = Slider 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value brightness.value colour.value contrast.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Slider 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; args = sortc larger [a, b]; image = args?0; patch = args?1; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = im_measure image.value 0 0 image.width image.height 6 4; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'); // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it linearising_lut = Image _linear_lut; // map the original image through the lineariser to // get linear 0-1 RGB image _image' = hist_map linearising_lut.value image.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ cast_float @ recomb M) _image'; // measure again and compute dE76 _camera'' = im_measure _result.value 0 0 image.width image.height 6 4; _dEs = map abs_vec (map Vector (_camera'' - _true_Lab).value); final_dE76 = mean (Vector _dEs); _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = _macbeth_names ? _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)"; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first args = sortc (const (is_instanceof calib_name)) [a, b]; calib = args?1; image = args?0; // map the original image through the lineariser to get // linear 0-1 RGB image image' = hist_map calib.linearising_lut image; // convert linear RGB camera to Lab result = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M image')); } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "building image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", len images != 2 = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = land (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Slider 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; blend_width = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 blend_width.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) blend_width.value { sorted = mosaic_sort mosaic_sort_lr [a, b]; a' = sorted?0; b' = sorted?1; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; blend_width = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 blend_width.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) blend_width.value { sorted = mosaic_sort mosaic_sort_tb [a, b]; a' = sorted?0; b' = sorted?1; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Slider 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; blend_width = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 blend_width.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top blend_width.value { sorted = mosaic_sort mosaic_sort_lr [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; blend_width = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 blend_width.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top blend_width.value { sorted = mosaic_sort mosaic_sort_tb [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if_then_else m_control im_in 0; _control_meanmax = so_meanmax _control_im; _group_check = is_instanceof "Group" m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Slider 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_instanceof "Group" m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize xfactor yfactor 1 scale_im; _offset_im = resize xfactor yfactor 1 offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.scale_cutoff, r1 >= Options.scale_cutoff]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. scale_cutoff = Slider 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = class Menuaction "_Adjust Tone Curve" "adjust tone curve on L*" { action x = class _result { _vislevel = 3; auto = Option "Set black and white point" [ "Manually", "Automatically from image histogram" ] 0; black = Slider 0 100 0; white = Slider 0 100 100; shadow_point = Slider 0.1 0.3 0.2; mid_point = Slider 0.4 0.6 0.5; highlight_point = Slider 0.7 0.9 0.8; shadow_adjust = Slider (-15) 15 0; mid_adjust = Slider (-30) 30 0; highlight_adjust = Slider (-15) 15 0; preview_curve = Image (im_tone_build black.value white.value shadow_point.value mid_point.value highlight_point.value shadow_adjust.value mid_adjust.value highlight_adjust.value); _result = map_unary process x { process image = tone_map curve lab { lab = colour_transform_to Image_type.LABQ image; curve = preview_curve, auto == 0 = tone_analyse shadow_point mid_point highlight_point shadow_adjust mid_adjust highlight_adjust lab; } } } } Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; // strange order forced on us by need to keep numbering backwards // compatible though 7.10 target_dpi = Option "Sharpen for print at" [ "300 dpi", "150 dpi", "75 dpi", "400 dpi" ] 0; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.10/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Slider" "make a new slider widget" { icon = "nip-slider-16.png"; action = Slider 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } ================================================ FILE: share/nip2/compat/7.10/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x, is_real x || is_image x = error (_ "bad arguments to " ++ "to_matrix") { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i && get_bands i == 1 = (im_vips2mask ((double) i'')).value, is_image i && get_bands i == 3 && get_width i == 1 = error (_ "not 1 band image, or 3 band 1 column image") { split = bandsplit i; i' = im_insert (split?0) (split?1) 1 0; i'' = im_insert i' (split?2) 2 0; } } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x, is_real x || is_image x = error (_ "bad arguments to " ++ "to_image") { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = error (_ "bad arguments to " ++ "to_colour") { colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. */ to_real x = to_real x.expr, is_Expression x = to_real x.value, is_class x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); /* Try to make a list ... ungroup, basically. Recurse for subgroups. */ to_list x = map to_list x.value, is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), len parts != 2 = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, len parts != 4 = (ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } // matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by // measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ ["D93", D93_whitepoint], ["D75", D75_whitepoint], ["D65", D65_whitepoint], ["D55", D55_whitepoint], ["D50", D50_whitepoint], ["A", A_whitepoint], ["B", B_whitepoint], ["C", C_whitepoint], ["E", E_whitepoint], ["D3250", D3250_whitepoint] ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* Convert D50 Lab to XYZ. */ im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (im_header_int "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab in = im_XYZ2Lab (im_sRGB2XYZ in); im_Lab2sRGB in = im_XYZ2sRGB (im_Lab2XYZ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* Given a list of things, try to make them all the same size. Don't change * the format. Don't touch non-image things. */ size_alike l = map enlarge l { max_width = foldr (test_prop has_width get_width) 0 l; max_height = foldr (test_prop has_height get_height) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); enlarge x = embed 0 0 0 max_width max_height x, has_width x = x; } /* Given a list of things, look for 1 band objects and bump them to to n - * band objects, where n is the maximum number of bands. Don't change the * format. Don't touch non-image things. */ bands_alike l = map upband l { max_bands = foldr (test_prop has_bands get_bands) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); upband x = bandjoin (replicate max_bands x), has_bands x && get_bands x == 1 = x; } ================================================ FILE: share/nip2/compat/7.10/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black (to_real w) (to_real h) (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = Matrix_con mask_g_sum 0 [mask_g_line] { mask_g = im_gauss_imask (radius / 3) 0.2; mask_g_line = mask_g.value?(mask_g.height / 2); mask_g_sum = foldr1 add mask_g_line; } /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (foldr1 add mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } ================================================ FILE: share/nip2/compat/7.10/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Slider 0.1 1 0.5; middle_section = Slider 0.1 1 0.2; blend_fraction = Slider 0.1 0.9 0.1; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height that * is extracted to produce each of the particular regions. */ corner_section = Slider 0.1 1 0.5; middle_section = Slider 0.1 1 0.3; blend_fraction = Slider 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Slider 0.1 1 0.4; edge_section = Slider 0.1 1 0.1; middle_section = Slider 0.1 0.5 0.3; blend_fraction = Slider 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount color" false; ls = Expression "Lower mount section bigger by (cm)" 0; colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 6; control_selection mask im no = (if mask then im * 1.2 else im * 1), no == 0 = (if mask then im * 0.8 else im * 1), no == 1 = (if mask then 0 else im), no == 2 = (if mask then 255 else im), no == 3 = (if mask then im else 0), no == 4 = (if mask then im else 255), no == 5 = mask; Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Slider 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = a.image; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = ((pt_list.value)?0).image; } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize f1 f2 1 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize f1 1 1 b1 {b1 = resize 1 f2 1 b;} } } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.10/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if_then_else mask im 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} } ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = im_header_int "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = im_header_int "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; } ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = im_header_int "Xsize" (get_image im), dir == 0 = im_header_int "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); } ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = line.image; //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_instanceof "Group" pt_list; pt_l = pt_list.value, group_check = pt_list; im = (pt_l?0).image; im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; } ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; } ================================================ FILE: share/nip2/compat/7.10/_list.def ================================================ /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn (tl l), fn (hd l) = l; /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l up from the left using function fn and start value st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold up list l, right to left, with function fn and start * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); /* Search a list for an element, returning it's index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* land l: and all the elements of list l together * * land (map (==0) list) == true, if every element of list is zero. * land :: [bool] -> bool */ land = foldr logical_and true; /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a = l?0; b = l?1; x = tl (tl l); } /* lor l: or all the elements of list l together * * lor (map (equal 0) list) == true, if any element of list is zero. * lor :: [bool] -> bool */ lor = foldr logical_or false; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map fn' (zip2 l1 l2) { fn' p = fn p?0 p?1; } /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map fn' (zip3 l1 l2 l3) { fn' p = fn p?0 p?1 p?2; } /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = lor (map (equal x) l); /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a = hd l; x = tl l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st (hd l)) (tl l); } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a = hd l1; x = tl l1; b = hd l2; y = tl l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by fn * * split is_space "hello world" == ["hello", "world"] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.10/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true of character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && land (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, land (map is_obj l) = true, land (map is_list l) && land (map (not @ is_obj) l) && land (map is_rectangular l) && len l > 0 && land (map (equal (hd lengths)) (tl lengths)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; lengths = map len l; } /* is_matrix l: is l a list of lists of real numbers, all the same length */ is_matrix l = is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [] = is_matrix l && len l == len (hd l); /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [] = is_matrix l && (len l) % 2 == 1 && (len (l?0)) % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && (len l) % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_Image x = is_instanceof "Image" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Slider x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && land (map xy l) { xy l = is_real_list l && len l == 2; } /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.B_W, bands == 1 = type, bands == 3 && is_colorimetric = Image_type.sRGB, bands == 3 && !is_colorimetric = Image_type.MULTIBAND, bands != 3 && !is_colorimetric = type { type = im_header_int "Type" im; coding = im_header_int "Coding" im; bands = im_header_int "Bands" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = im_header_int "BandFmt" x, is_image x = error ("get_format: unable to get format from " ++ print x); has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = im_header_int "Bbits" x, is_image x = error ("get_bits: unable to get bits from " ++ print x); has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = im_header_int "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = error ("get_bands: unable to get bands from " ++ print x); has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = im_header_int "Coding" x, is_image x = Image_coding.NOCODING, is_real x = error ("get_coding: unable to get coding from " ++ print x); has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = im_header_int "Xres" x, is_image x = error ("get_xres: unable to get xres from " ++ print x); has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = im_header_int "Yres" x, is_image x = error ("get_yres: unable to get yres from " ++ print x); has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = im_header_int "Xoffset" x, is_image x = error ("get_xoffset: unable to get xoffset from " ++ print x); has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = im_header_int "Yoffset" x, is_image x = error ("get_yoffset: unable to get yoffset from " ++ print x); has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = error ("get_image: unable to get image from " ++ print x); has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = error ("get_number: unable to get number from " ++ print x); has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = error ("get_real: unable to get real from " ++ print x); has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = im_header_int "Xsize" x, is_image x = error ("get_width: unable to get width from " ++ print x); has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = im_header_int "Ysize" x, is_image x = error ("get_height: unable to get height from " ++ print x); has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = error ("get_left: unable to get left from " ++ print x); has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = error ("get_top: unable to get top from " ++ print x); // like has/get member,but first in a lst of objects has_member_list has objects = filter has objects != []; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } ================================================ FILE: share/nip2/compat/7.10/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; join a b = a ++ b; subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; if_then_else a b c = if a then b else c; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error "unimplemented vector operation" { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } /* Macbeth chart patch names. */ _macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = im_header_int "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "mean") { mean_op = Operator "mean" mean_object Operator_type.COMPOUND false; mean_object x = im_avg x, is_image x = mean_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "mean"); mean_list l = s / n { totals = sum l; n = totals?0; s = totals?1; } // return [n, sum] for a list of numbers, or a list of list of num // etc. sum x = foldr accumulate [0, 0] x { accumulate x sofar = [n + 1, x + s], is_real x = [n + n', s + s'], is_list x = error "mean_list: not real or [real]" { n = sofar?0; s = sofar?1; sub_acc = sum x; n' = sub_acc?0; s' = sub_acc?1; } } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation_object Operator_type.COMPOUND false; deviation_object x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation"); deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { totals = sum_sum2_list l; n = totals?0; s = totals?1; s2 = totals?2; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { n = sofar?0; s = sofar?1; s2 = sofar?2; sub_acc = sum_sum2_list x; n' = sub_acc?0; s' = sub_acc?1; s2' = sub_acc?2; } } } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs_object Operator_type.COMPOUND false; abs_object x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs"); abs_list l = (foldr1 add (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec_object Operator_type.COMPOUND false; abs_vec_object x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (Vector (map abs_vec_list x)), is_matrix x = error (_ "bad arguments to " ++ "abs_vec"); abs_vec_list l = (foldr1 add (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (foldr1 add (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_matrix x, is_list x && is_list (hd x) = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose_object Operator_type.COMPOUND_REWRAP false; transpose_object x = transpose_matrix x, is_matrix x = transpose_image x, is_image x = error (_ "bad arguments to " ++ "transpose"); transpose_matrix l = [], l' == [] = (map hd l') : (transpose_matrix (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; rot90 x = oo_unary_function rot90_op x, is_class x = im_rot90 x, is_image x = error (_ "bad arguments to " ++ "rot90") { rot90_op = Operator "rot90" rot90_object Operator_type.COMPOUND_REWRAP false; rot90_object x = rot90_matrix x, is_matrix x = im_rot90 x, is_image x = error (_ "bad arguments to " ++ "rot90"); // slow, but what the heck // avoid im_rotate_dmask90(), it can only do square odd-sided matricies rot90_matrix l = apply_matrix_as_image im_rot90 l; } rot180 x = oo_unary_function rot180_op x, is_class x = im_rot180 x, is_image x = error (_ "bad arguments to " ++ "rot180") { rot180_op = Operator "rot180" rot180_object Operator_type.COMPOUND_REWRAP false; rot180_object x = rot180_matrix x, is_matrix x = im_rot180 x, is_image x = error (_ "bad arguments to " ++ "rot180"); // slow, but what the heck rot180_matrix l = apply_matrix_as_image im_rot180 l; } rot270 x = oo_unary_function rot270_op x, is_class x = im_rot270 x, is_image x = error (_ "bad arguments to " ++ "rot270") { rot270_op = Operator "rot270" rot270_object Operator_type.COMPOUND_REWRAP false; rot270_object x = rot270_matrix x, is_matrix x = im_rot270 x, is_image x = error (_ "bad arguments to " ++ "rot270"); // slow, but what the heck rot270_matrix l = apply_matrix_as_image im_rot270 l; } image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (im_header_double "Xres" x) (im_header_double "Yres" x) (im_header_int "Xoffset" x) (im_header_int "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (im_header_int "Type" x) (im_header_double "Xres" x) (im_header_double "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } rotquad x = oo_unary_function rotquad_op x, is_class x = im_rotquad x, is_image x = error (_ "bad arguments to " ++ "rotquad") { rotquad_op = Operator "rotquad" rotquad_object Operator_type.COMPOUND_REWRAP false; rotquad_object x = rotquad_matrix x, is_matrix x = im_rotquad x, is_image x = error (_ "bad arguments to " ++ "rotquad"); rotquad_matrix l = apply_matrix_as_image im_rotquad l; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } fliptb x = oo_unary_function fliptb_op x, is_class x = im_flipver x, is_image x = error (_ "bad arguments to " ++ "fliptb") { fliptb_op = Operator "fliptb" fliptb_object Operator_type.COMPOUND_REWRAP false; fliptb_object x = fliptb_matrix x, is_matrix x = im_flipver x, is_image x = error (_ "bad arguments to " ++ "fliptb"); fliptb_matrix l = reverse l; } fliplr x = oo_unary_function fliplr_op x, is_class x = im_fliphor x, is_image x = error (_ "bad arguments to " ++ "fliplr") { fliplr_op = Operator "fliplr" fliplr_object Operator_type.COMPOUND_REWRAP false; fliplr_object x = fliplr_matrix x, is_matrix x = im_fliphor x, is_image x = error (_ "bad arguments to " ++ "fliplr"); fliplr_matrix l = map reverse l; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_real_list x || is_matrix x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_matrix x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_real_list x || is_matrix x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_matrix x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } recomb matrix image = colour_unary recomb_op image { recomb_op x = im_recomb x (to_matrix matrix), is_image x = error (_ "bad arguments to " ++ "recomb"); } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (converse cons [] @ converse subscript x') obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = im_header_int "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend") { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { then_part = x?0; else_part = x?1; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = x, is_image x = x.value, is_Image x = black + x { black = im_black target_width target_height target_bands; } then_image = to_image then_part; else_image = to_image else_part; then_image' = clip2fmt target_format then_image; else_image' = clip2fmt target_format else_image; resized = size_alike [cond, then_image', else_image']; blend_result_image = image_set_type target_type (im_blend resized?0 resized?1 resized?2); } } insert x y small big = oo_binary_function insert_op small big, is_class small = oo_binary'_function insert_op small big, is_class big = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (_ "bad arguments to " ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_similarity image (cos angle) (sin angle) 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" rotate Operator_type.COMPOUND_REWRAP false; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = im_header_int "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { image = (unsigned char) im_mask2vips (Matrix m2); m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x // work for groups too (convenient) = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } hist_find image = oo_unary_function hist_find_op image, is_class image = im_histgr image (-1), is_image image = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" hist_find Operator_type.COMPOUND_REWRAP false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { hist_map_op = Operator "hist_map" hist_map Operator_type.COMPOUND_REWRAP false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; sum = foldr1 add; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } resize xfac yfac interp image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, nearest neighbour // can't really do this right ... upscale by integer part, then // bilinear to exact size = scale (break xfac')?1 (break yfac')?1 (im_zoom im (break xfac')?0 (break yfac')?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by any factor, nearest neighbour // can't really do this right ... downscale by integer part, // then bilinear to exact size = scale (1 / (break rxfac')?1) (1 / (break ryfac')?1) (im_subsample im (break rxfac')?0 (break ryfac')?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, bilinear = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 && interp == Interpolate.BILINEAR // downscale by any factor, bilinear // block shrink by integer factor, then bilinear resample to // exact = scale (1 / (break rxfac')?1) (1 / (break ryfac')?1) (im_shrink im (break rxfac')?0 (break ryfac')?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.BILINEAR = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate.names.lookup 1 0 interp ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // binlinear resize scale xfac yfac im = im_affine im xfac 0 0 yfac 0 0 0 0 (rint (get_width im * xfac)) (rint (get_height im * yfac)); } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (tone_analyse s m h sa ma ha) Operator_type.COMPOUND_REWRAP false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Image @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (converse remainder base) (takewhile (not_equal 0) (iterate (converse idiv base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); idiv a b = (int) (a / b); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map a 3-ary function over three objects. */ map_trinary fn a b c = wrap (map3 (map_trinary fn) a' b' c'), is_list a' && is_list b' && is_list c' = wrap (map2 (map_trinary fn a') b' c'), is_list b' && is_list c' = wrap (map2 (map_trinary (converse31 fn) b') a' c'), is_list a' && is_list c' = wrap (map2 (map_trinary (converse32 fn) c') a' b'), is_list a' && is_list b' = wrap (map (map_trinary fn a' b') c'), is_list c' = wrap (map (map_trinary (converse32 fn) a' c') b'), is_list b' = wrap (map (map_trinary (converse34 fn) b' c') a'), is_list a' = fn a b c { converse31 fn a b c = fn b a c; converse32 fn a b c = fn c a b; converse33 fn a b c = fn a c b; converse34 fn a b c = fn b c a; a' = a.value, is_Group a = a; b' = b.value, is_Group b = b; c' = c.value, is_Group c = c; wrap = Group, is_Group a || is_Group b || is_Group c = id; } /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = wrap (map2 (map_binary fn) a' b'), is_list a' && is_list b' = wrap (map (map_binary fn a') b'), is_list b' = wrap (map (map_binary (converse fn) b') a'), is_list a' = fn a b { a' = a.value, is_Group a = a; b' = b.value, is_Group b = b; wrap = Group, is_Group a || is_Group b = id; } /* Map a 1-ary function over an object. */ map_unary fn a = wrap (map (map_unary fn) a'), is_list a' = fn a { a' = a.value, is_Group a = a; wrap = Group, is_Group a = id; } /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. height] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Calculate padding ... pad up to tile_size pixel boundary. */ sx = tile_width' + (width - width % hstep); sy = tile_height' + (height - height % vstep); /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. width] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } ================================================ FILE: share/nip2/compat/7.10/_types.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0, 1, 2 or 3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_Matrix_width w = [is_Matrix_width, _ "Matrix, " ++ print w ++ _ " columns"] { is_Matrix_width x = is_Matrix x && x.width == w; } check_colour_space = [is_colour_space, "colour_space"]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide or VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down a bit. */ check_args x = error message, badargs != [] || badalls != [] = check_args x.super, x.super != [] = x { argcheck = x._check_args; allcheck = x._check_all; // join two strings up with a separator string join_sep j a b = a ++ j ++ b; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad arguments to " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "usage" ++ "\n" ++ indent ++ usage ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make usage note usage = x.name ++ " " ++ foldr (join_sep " ") [] (map (extract 1) argcheck); // make arg type notes arg_types = foldr (join_sep "\n") [] (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = foldr (join_sep "\n") [] all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; /* Provide a fallback for class == thing ... just use pointer * equality. */ oo_binary_table op x = [ [pointer_equal this x, op.op_name == "equal" || op.op_name == "equal'"], [not_pointer_equal this x, op.op_name == "not_equal" || op.op_name == "not_equal'"] ]; oo_unary_table op = []; } /* A list of things. Do automatic iteration of unary and binary operators on us. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [this.Group (map2 (apply2 op) value x.value), is_Group x], [this.Group (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [this.Group (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [if value then x?0 else x?1, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real ... handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binary op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binary op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binary op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binary op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unary op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unary op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binary op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binary op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binary op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unary op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [len value == 3, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = im_header_int "Type" im; bands = im_header_int "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.value_name colour.expr { _vislevel = 3; space = Option_enum Image_type.colour_spaces "Colour space" default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Compat. slider type. */ Slider = Scale ""; /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } Option_enum enum caption value_name = class Option caption enum.names (index (equal value_name) enum.names) { // corresponding thing value_thing = enum.get_thing value_name; Option_edit caption labels value = this.Option_enum enum caption (enum.names ? value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NOCODING = 0; COLQUANT = 1; LABPACK = 2; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* present col x: is there an x in column col */ present col x = member (map (extract col) value) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (map (extract from) value); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = map (extract 0) value; things = map (extract 1) value; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { FOURIER = 24; YXY = 23; sRGB = 22; LABS = 21; LCH = 19; UCS = 18; RGB = 17; LABQ = 16; CMYK = 15; CMC = 14; LAB = 13; XYZ = 12; LUT = 11; HISTOGRAM = 10; POWER_SPECTRUM = 9; BLUE_ONLY = 8; GREEN_ONLY = 7; RED_ONLY = 6; YUV = 5; IR = 4; XRAY = 3; LUMINACE = 2; B_W = 1; MULTIBAND = 0; /* Table to get names <-> numbers. */ type_names = Enum [ ["FOURIER", FOURIER], ["YXY", YXY], ["sRGB", sRGB], ["LABS", LABS], ["LCH", LCH], ["UCS", UCS], ["RGB", RGB], ["LABQ", LABQ], ["CMYK", CMYK], ["CMC", CMC], ["LAB", LAB], ["XYZ", XYZ], ["LUT", LUT], ["HISTOGRAM", HISTOGRAM], ["POWER_SPECTRUM", POWER_SPECTRUM], ["BLUE_ONLY", BLUE_ONLY], ["GREEN_ONLY", GREEN_ONLY], ["RED_ONLY", RED_ONLY], ["YUV", YUV], ["IR", IR], ["XRAY", XRAY], ["LUMINACE", LUMINACE], ["B_W", B_W], ["MULTIBAND", MULTIBAND] ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ ["sRGB", sRGB], ["Lab", LAB], ["LCh", LCH], ["XYZ", XYZ], ["Yxy", YXY], ["UCS", UCS] ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ ["Mono", B_W], ["sRGB", sRGB], ["Lab", LAB], ["LabQ", LABQ], ["LabS", LABS], ["LCh", LCH], ["XYZ", XYZ], ["Yxy", YXY], ["UCS", UCS] ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = im_header_double "Xres" value; yres = im_header_double "Yres" value; xoffset = im_header_int "Xoffset" value; yoffset = im_header_int "Yoffset" value; filename = im_header_string "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], // image ++ image is slightly different ... we want to // sizealike, but we must not bandalike [wrap (op.fn (get_image resized?0) (get_image resized?1)), has_image x && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], // arithmetic and reational binops between image resize // and band_alike images to match [wrap (op.fn (get_image rebanded?0) (get_image rebanded?1)), has_image x && (op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL)], // other op types don't resize [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], [wrap (op.fn this.value x), true] ] ++ super.oo_binary_table op x { // wrap the result with this ... only skip rewrap for COMPOUND wrap = id, op.type == Operator_type.COMPOUND = this.Image; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } then_part = x?0; else_part = x?1; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = x, is_image x = x.value, is_Image x = black + x { black = im_black width height target_bands; } then_image = to_image then_part; else_image = to_image else_part; then_image' = clip2fmt target_format then_image; else_image' = clip2fmt target_format else_image; ite_resized = size_alike [value, then_image', else_image']; ite_result_image = image_set_type target_type (if ite_resized?0 then ite_resized?1 else ite_resized?2); resized = size_alike [this, x]; rebanded = bands_alike resized; } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; /* Table to map interpol numbers to descriptive strings */ names = Enum [ [_ "Nearest neighbour", NEAREST_NEIGHBOUR], [_ "Bilinear", BILINEAR], [_ "Bicubic", BICUBIC] ]; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ [_ "Perceptual", PERCEPTUAL], [_ "Relative", RELATIVE], [_ "Saturation", SATURATION], [_ "Absolute", ABSOLUTE] ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} ================================================ FILE: share/nip2/compat/7.12/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; _L = default_value?0; _a = default_value?1; _b = default_value?2; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Convert to") (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Tag as") (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Colour Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum Whitepoints (_ "Old whitepoint") "D65"; new_white = Option_enum Whitepoints (_ "New whitepoint") "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum Render_intent.names (_ "Render intent") (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; _result = map_unary chart x { chart in = measure 0 0 in.width in.height (to_real pacross) (to_real pdown) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.12/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_convf, !separable && type == 1 = im_convsepf, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 5) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height + 1) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale Blend" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image Blend" "use an image to blend two objects" { action a b c = map_trinary process a b c { process a b c = blend condition in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; args' = sortc compare [a, b, c]; condition = args'?0; in1 = args'?1; in2 = args'?2; } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha Blend" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ ["Normal", NORMAL], ["If Lighter", IFLIGHTER], ["If Darker", IFDARKER], ["Multiply", MULTIPLY], ["Add", ADD], ["Subtract", SUBTRACT], ["Screen", SCREEN], ["Burn", BURN], ["Soft Light", SOFTLIGHT], ["Hard Light", HARDLIGHT], ["Lighten", LIGHTEN], ["Darken", DARKEN], ["Dodge", DODGE], ["Reflect", REFLECT], ["Freeze", FREEZE], ["Bitwise OR", OR], ["Bitwise AND", AND] ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum names "Blend mode" "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first args = sortc (const (is_colour_type @ get_type)) [a, b]; mono = args?0; colour = args?1; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ "Grey", "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } Filter_draw_line_item = class Menuaction "Draw _Line" "draw a line using an arrow as a guide" { } ================================================ FILE: share/nip2/compat/7.12/Format.def ================================================ Format_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Format_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Format_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Format_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Format_assemble_item = class Menuaction "_Assemble Objects" "assemble a list (or group) of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } #separator Format_csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Interpret_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } ================================================ FILE: share/nip2/compat/7.12/Histogram.def ================================================ Hist_new_item = class Menuaction "_New Histogram" "make a new histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type field to Histogram" { action x = hist_tag x; } #separator Hist_find_item = class Menupullright "_Find Histogram" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } } Hist_map_item = class Menuaction "_Map Histogram" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { args = sortc (const is_hist) [a, b]; im = args?0; hist = args?1; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Cumulativise Histogram" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate Histogram" "find point-to-point differences (inverse of Cumulativise)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise Histogram" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_match_item = class Menuaction "Ma_tch Histogram" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); // extract that area area = extract_area (re p' + displace.value) (im p' - width.value / 2 + vdisplace.value) (re pv) width.value im'; // squish vertically to get an average area' = resize 1 (1 / width.value) Interpolate.BILINEAR area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = area { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); // extract that area area = extract_area (re p' + displace.value) (im p' - width.value / 2 + vdisplace.value) (re pv) width.value im'; } } } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; format = Option_enum Plot_format.names "Format" "YYYY"; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (image x) { options = [["style", style.value], ["format", format.value]] ++ range; range = [], auto = [["xmin", xmin.expr], ["xmax", xmax.expr], ["ymin", ymin.expr], ["ymax", ymax.expr]]; image x = image (extract_arrow x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } extract_arrow arrow = extract_area (re p') (im p') (re pv) 1 im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } } } } ================================================ FILE: share/nip2/compat/7.12/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum Image_type.type_names "Image type" "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; args' = sortc compare [a, b]; target = args'?0; x = args'?1; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } #separator Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ ["width", width], ["height", height], ["bands", bands], ["format", format], ["type", type], ["xres", xres], ["yres", yres], ["xoffset", xoffset], ["yoffset", yoffset], ["coding", coding] ]; field_option name = Option_enum field_names (_ "Field") name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum Image_type.type_names "Image type" (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_number_format_item = class Menupullright "Set _Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; _result = map_unary process x { process image = rotate angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = map_unary straighten x { straighten arrow = rotate angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { _interp = Option_enum Interpolate.names "Interpolation" "Bilinear"; Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = _interp; _result = map_unary process x { process image = resize xfactor yfactor interp.value_thing image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; interp = _interp; _result = map_unary process x { process image = resize fac fac interp.value_thing image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = max_pair xfac yfac; min_factor = min_pair xfac yfac; fac = [max_factor, min_factor, xfac, yfac]?which; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.BILINEAR; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _transform { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform _result = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image _result?0; _transform = Transform _result?1 reference.width reference.height; final_error = _result?2; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first args = sortc (const is_Transform) [a, b]; i = args?0; t = args?1; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } sep1 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldr1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; _sorted = sortc _pred [a, b]; _a' = _sorted?0; _b' = _sorted?1; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 2; ssize = Expression "Step size" 2; _result = Image (colour_transform_to (get_type start) im) { base = colour_transform_to Image_type.LAB start; step = to_real ssize; offset = to_real nstep * step; range c = [c - offset, c - offset + step ... c + offset]; mk_patch b a = image_new 150 150 3 Image_format.FLOAT Image_coding.NOCODING Image_type.LAB (Vector [base?0, a, b]) 0 0; mk_strip b = map (mk_patch b) (range base?1); mk_grid = map mk_strip (range base?2); im = imagearray_assemble 15 15 mk_grid; } } } } ================================================ FILE: share/nip2/compat/7.12/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.12 start_DATA = \ Math.def \ Image.def \ Colour.def \ Tasks.def \ Format.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ Preferences.ws \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.12/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_And" "bitwise and of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_Or" "bitwise or of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "E_xclusive Or" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_Not" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Band_or_item = class Menuaction "Band O_r" "or the bands of an image together" { action im = map_unary (foldr1 bitwise_or @ bandsplit) im; } Band_and_item = class Menuaction "Band A_nd" "and the bands of an image together" { action im = map_unary (foldr1 bitwise_and @ bandsplit) im; } } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; // from s15.2, p 665 NR in C Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = class { _vislevel = 3; /* Hide these by default. */ details = class { ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; t = map (converse subtract sxoss) xes; st2 = sum (map (converse power 2) t); } slope = sum (map2 multiply details.t yes) / details.st2; intercept = (details.sy - details.sx * slope) / details.ss; chi2 = sum (map (converse power 2) (map2 subtract (map (converse subtract intercept) yes) (map (multiply slope) xes))); siga = (chi2 / (details.ss - 2)) ** 0.5 * ((1 + details.sx ** 2 / (details.ss * details.st2)) / details.ss) ** 0.5; sigb = (chi2 / (details.ss - 2)) ** 0.5 * (1 / details.st2) ** 0.5; q = 1.0; } } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = class { _vislevel = 3; /* Hide these by default. */ details = class { wt = map (converse power (-0.5)) devs; ss = sum wt; sx = sum (map2 multiply xes wt); sy = sum (map2 multiply yes wt); sxoss = sx / ss; t = map2 divide (map (converse subtract sxoss) xes) devs; st2 = sum (map (converse power 2) t); } slope = sum (map2 divide (map2 multiply details.t yes) devs) / details.st2; intercept = (details.sy - details.sx * slope) / details.ss; siga = ((1 + details.sx * details.sx / (details.ss * details.st2)) / details.ss) ** 0.5; sigb = (1 / details.st2) ** 0.5; chi2 = sum (map (converse power 2) (map2 divide (map2 subtract (map (converse subtract intercept) yes) (map (multiply slope) xes)) devs)); q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.12/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 (converse join_tb) (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 (converse join_lr) (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 (converse join_tb) (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 (converse join_lr) (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [["style", Plot_style.POINT], ["format", Plot_format.XYYY]] ++ range; range = [], auto = [["xmin", xmin.expr], ["xmax", xmax.expr], ["ymin", ymin.expr], ["ymax", ymax.expr]]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.12/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/7.12/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; args = sortc larger [a, b]; image = args?0; patch = args?1; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = im_measure image.value 0 0 image.width image.height 6 4; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'); // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it linearising_lut = Image _linear_lut; // map the original image through the lineariser to // get linear 0-1 RGB image _image' = hist_map linearising_lut.value image.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ cast_float @ recomb M) _image'; // measure again and compute dE76 _camera'' = im_measure _result.value 0 0 image.width image.height 6 4; _dEs = map abs_vec (_camera'' - _true_Lab).value; final_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 = "Unknown"; } } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first args = sortc (const (is_instanceof calib_name)) [a, b]; calib = args?1; image = args?0; // map the original image through the lineariser to get // linear 0-1 RGB image image' = hist_map calib.linearising_lut image; // convert linear RGB camera to Lab result = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M image')); } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [["style", style.value]] ++ if auto then [] else [["ymin", ymin.expr], ["ymax", ymax.expr]]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", len images != 2 = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = land (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { sorted = mosaic_sort mosaic_sort_lr [a, b]; a' = sorted?0; b' = sorted?1; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { sorted = mosaic_sort mosaic_sort_tb [a, b]; a' = sorted?0; b' = sorted?1; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { sorted = mosaic_sort mosaic_sort_lr [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { sorted = mosaic_sort mosaic_sort_tb [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize xfactor yfactor 1 scale_im; _offset_im = resize xfactor yfactor 1 offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.12/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.12/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && len x == 3 = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The innermost list objects become Group()'d. */ to_group x = Group x, is_list x && !contains_Group x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), len parts != 2 = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, len parts != 4 = (ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), len parts != 5 = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ ["D93", D93_whitepoint], ["D75", D75_whitepoint], ["D65", D65_whitepoint], ["D55", D55_whitepoint], ["D50", D50_whitepoint], ["A", A_whitepoint], ["B", B_whitepoint], ["C", C_whitepoint], ["E", E_whitepoint], ["D3250", D3250_whitepoint] ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, im_8216 @ im_mono2sRGB], [B_W, GREY16, im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, im_8216], [RGB, GREY16, im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, im_8216], [sRGB, GREY16, im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* Given a list of things, try to make them all the same size. Don't change * the format. Don't touch non-image things. */ size_alike l = map enlarge l { max_width = foldr (test_prop has_width get_width) 0 l; max_height = foldr (test_prop has_height get_height) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); enlarge x = embed 0 0 0 max_width max_height x, has_width x = x; } /* Given a list of things, look for 1 band objects and bump them to to n - * band objects, where n is the maximum number of bands. Don't change the * format. Don't touch non-image things. */ bands_alike l = map upband l { max_bands = foldr (test_prop has_bands get_bands) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); upband x = bandjoin (replicate max_bands x), has_bands x && get_bands x == 1 = x; } /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = foldl1 fn l { fn a b = a ++ path_separator ++ b; } /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 == 2 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.12/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black (to_real w) (to_real h) (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = Matrix_con mask_g_sum 0 [mask_g_line] { mask_g = im_gauss_imask (radius / 3) 0.2; mask_g_line = mask_g.value?(mask_g.height / 2); mask_g_sum = sum mask_g_line; } /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } ================================================ FILE: share/nip2/compat/7.12/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height that * is extracted to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount color" false; ls = Expression "Lower mount section bigger by (cm)" 0; colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 6; control_selection mask im no = (if mask then im * 1.2 else im * 1), no == 0 = (if mask then im * 0.8 else im * 1), no == 1 = (if mask then 0 else im), no == 2 = (if mask then 255 else im), no == 3 = (if mask then im else 0), no == 4 = (if mask then im else 255), no == 5 = mask; Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = a.image; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = ((pt_list.value)?0).image; } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize f1 f2 1 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize f1 1 1 b1 {b1 = resize 1 f2 1 b;} } } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.12/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} } ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; } ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); } ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = line.image; //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = (pt_l?0).image; im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; } ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; } ================================================ FILE: share/nip2/compat/7.12/_list.def ================================================ /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn (tl l), fn (hd l) = l; /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); sum = foldr1 add; product = foldr1 multiply; /* Search a list for an element, returning it's index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* land l: and all the elements of list l together * * land (map (==0) list) == true, if every element of list is zero. * land :: [bool] -> bool */ land = foldr logical_and true; /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a = l?0; b = l?1; x = tl (tl l); } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* lor l: or all the elements of list l together * * lor (map (equal 0) list) == true, if any element of list is zero. * lor :: [bool] -> bool */ lor = foldr logical_or false; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = lor (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge b l r = [], b == [] || l == [] || r == [] = hd l : merge (tl b) (tl l) (tl r), hd b = hd r : merge (tl b) (tl l) (tl r); /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a = hd l; x = tl l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st (hd l)) (tl l); } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a = hd l1; x = tl l1; b = hd l2; y = tl l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.12/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = lor (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && land (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, land (map is_obj l) = true, land (map is_list l) && land (map (not @ is_obj) l) && land (map is_rectangular l) && len l > 0 && land (map (equal (hd lengths)) (tl lengths)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; lengths = map len l; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && len l == len (hd l); /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && land (map xy l) { xy l = is_real_list l && len l == 2; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && lor (map is_Group l) = lor (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = type, bands == 1 && type == Image_type.GREY16 = type, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, bands == 1 = type, bands == 3 && is_colorimetric = Image_type.sRGB, bands == 3 && !is_colorimetric = Image_type.MULTIBAND, bands != 3 && !is_colorimetric = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = error ("get_format: unable to get format from " ++ print x); has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = error ("get_bits: unable to get bits from " ++ print x); has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = error ("get_bands: unable to get bands from " ++ print x); has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = error ("get_coding: unable to get coding from " ++ print x); has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = error ("get_xres: unable to get xres from " ++ print x); has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = error ("get_yres: unable to get yres from " ++ print x); has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = error ("get_xoffset: unable to get xoffset from " ++ print x); has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = error ("get_yoffset: unable to get yoffset from " ++ print x); has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = error ("get_image: unable to get image from " ++ print x); has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = error ("get_number: unable to get number from " ++ print x); has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = error ("get_real: unable to get real from " ++ print x); has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = error ("get_width: unable to get width from " ++ print x); has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = error ("get_height: unable to get height from " ++ print x); has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = error ("get_left: unable to get left from " ++ print x); has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = error ("get_top: unable to get top from " ++ print x); // like has/get member,but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.12/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error "unimplemented vector operation" { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { totals = sum_sum2_list l; n = totals?0; s = totals?1; s2 = totals?2; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { n = sofar?0; s = sofar?1; s2 = sofar?2; sub_acc = sum_sum2_list x; n' = sub_acc?0; s' = sub_acc?1; s2' = sub_acc?2; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { totals = sum_sum2_list l; n = totals?0; s = totals?1; s2 = totals?2; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { n = sofar?0; s = sofar?1; s2 = sofar?2; sub_acc = sum_sum2_list x; n' = sub_acc?0; s' = sub_acc?1; s2' = sub_acc?2; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (converse cons [] @ converse subscript x') obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend") { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { then_part = x?0; else_part = x?1; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = x, is_image x = x.value, is_Image x = black + x { black = im_black target_width target_height target_bands; } then_image = to_image then_part; else_image = to_image else_part; then_image' = clip2fmt target_format then_image; else_image' = clip2fmt target_format else_image; resized = size_alike [cond, then_image', else_image']; blend_result_image = image_set_type target_type (im_blend resized?0 resized?1 resized?2); } } insert x y small big = oo_binary_function insert_op small big, is_class small = oo_binary'_function insert_op small big, is_class big = im_insert rebanded?1 rebanded?0 (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; rebanded = bands_alike [small, big]; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand rebanded?1 rebanded?0 (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; rebanded = bands_alike [small, big]; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (_ "bad arguments to " ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_similarity image (cos angle) (sin angle) 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" rotate Operator_type.COMPOUND_REWRAP false; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join a b { join_lr_op = Operator "join_lr" join Operator_type.COMPOUND_REWRAP false; join a b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr"); join_im a b = insert (get_width b) 0 a b; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join a b { join_tb_op = Operator "join_tb" join Operator_type.COMPOUND_REWRAP false; join a b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb"); join_im a b = insert 0 (get_height b) a b; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { image = (unsigned char) im_mask2vips (Matrix m2); m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsepf image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = conv (Matrix [[-1, 1]]) h; } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize xfac yfac interp image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, nearest neighbour // can't really do this right ... upscale by integer part, then // bilinear to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by any factor, nearest neighbour // can't really do this right ... downscale by integer part, // then bilinear to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, bilinear = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 && interp == Interpolate.BILINEAR // downscale by any factor, bilinear // block shrink by integer factor, then bilinear resample to // exact = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.BILINEAR = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate.names.lookup 1 0 interp ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // binlinear resize scale xfac yfac im = im_affine im xfac 0 0 yfac 0 0 0 0 (rint (get_width im * xfac)) (rint (get_height im * yfac)); } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } flood_blob x y v image = oo_unary_function flood_blob_op image, is_class image = im_flood_blob_copy image (to_real x) (to_real y) v, is_image image = error (_ "bad arguments to " ++ "flood_blob") { flood_blob_op = Operator "flood_blob" (flood_blob x y v) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (converse remainder base) (takewhile (not_equal 0) (iterate (converse idiv base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); idiv a b = (int) (a / b); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = [], lor (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == []; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Image (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && len x?0 > 1 = error (_ "bad arguments to " ++ "buildlut"); ================================================ FILE: share/nip2/compat/7.12/_types.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0, 1, 2 or 3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, "colour_space"]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide or VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down a bit. */ check_args x = error message, badargs != [] || badalls != [] = check_args x.super, x.super != [] = x { argcheck = x._check_args; allcheck = x._check_all; // join two strings up with a separator string join_sep j a b = a ++ j ++ b; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad arguments to " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "usage" ++ "\n" ++ indent ++ usage ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make usage note usage = x.name ++ " " ++ foldr (join_sep " ") [] (map (extract 1) argcheck); // make arg type notes arg_types = foldr (join_sep "\n") [] (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = foldr (join_sep "\n") [] all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; /* Provide a fallback for class == thing ... just use pointer * equality. */ oo_binary_table op x = [ [pointer_equal this x, op.op_name == "equal" || op.op_name == "equal'"], [not_pointer_equal this x, op.op_name == "not_equal" || op.op_name == "not_equal'"] ]; oo_unary_table op = []; } /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary ((converse op.fn) x) this, true] ] { // need ite as a true trinary ite a b c = if a then b else c; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = [], lor (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == []; } } map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } oo_unary_table op = [ [map_unary op.fn this, true] ] { // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = [], lor (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == []; } } map_unary fn a = map_nary (list_1ary fn) [a]; } } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ]; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ]; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ]; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ]; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [len value == 3, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.value_name colour.expr { _vislevel = 3; space = Option_enum Image_type.colour_spaces "Colour space" default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } Option_enum enum caption value_name = class Option caption enum.names (index (equal value_name) enum.names) { // corresponding thing value_thing = enum.get_thing value_name; Option_edit caption labels value = this.Option_enum enum caption (enum.names ? value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NOCODING = 0; COLQUANT = 1; LABPACK = 2; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* present col x: is there an x in column col */ present col x = member (map (extract col) value) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (map (extract from) value); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = map (extract 0) value; things = map (extract 1) value; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; LUMINANCE = 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; /* Table to get names <-> numbers. */ type_names = Enum [ ["MULTIBAND", MULTIBAND], ["B_W", B_W], ["LUMINANCE", LUMINANCE], ["XRAY", XRAY], ["IR", IR], ["YUV", YUV], ["RED_ONLY", RED_ONLY], ["GREEN_ONLY", GREEN_ONLY], ["BLUE_ONLY", BLUE_ONLY], ["POWER_SPECTRUM", POWER_SPECTRUM], ["HISTOGRAM", HISTOGRAM], ["LUT", LUT], ["XYZ", XYZ], ["LAB", LAB], ["CMC", CMC], ["CMYK", CMYK], ["LABQ", LABQ], ["RGB", RGB], ["UCS", UCS], ["LCH", LCH], ["LABS", LABS], ["sRGB", sRGB], ["YXY", YXY], ["FOURIER", FOURIER], ["RGB16", RGB16], ["GREY16", GREY16] ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ ["sRGB", sRGB], ["Lab", LAB], ["LCh", LCH], ["XYZ", XYZ], ["Yxy", YXY], ["UCS", UCS] ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ ["Mono", B_W], ["sRGB", sRGB], ["RGB16", RGB16], ["GREY16", GREY16], ["Lab", LAB], ["LabQ", LABQ], ["LabS", LABS], ["LCh", LCH], ["XYZ", XYZ], ["Yxy", YXY], ["UCS", UCS] ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], // image ++ image is slightly different ... we want to // sizealike, but we must not bandalike [wrap (op.fn (get_image resized?0) (get_image resized?1)), has_image x && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], // arithmetic and reational binops between image resize // and band_alike images to match [wrap (op.fn (get_image rebanded?0) (get_image rebanded?1)), has_image x && (op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL)], // other op types don't resize [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. "Image v == // 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } then_part = x?0; else_part = x?1; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = x, is_image x = x.value, is_Image x = black + x { black = im_black width height target_bands; } then_image = to_image then_part; else_image = to_image else_part; then_image' = clip2fmt target_format then_image; else_image' = clip2fmt target_format else_image; ite_resized = size_alike [value, then_image', else_image']; ite_result_image = image_set_type target_type (if ite_resized?0 then ite_resized?1 else ite_resized?2); resized = size_alike [this, x]; rebanded = bands_alike resized; } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; /* Table to map interpol numbers to descriptive strings */ names = Enum [ [_ "Nearest neighbour", NEAREST_NEIGHBOUR], [_ "Bilinear", BILINEAR], [_ "Bicubic", BICUBIC] ]; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ [_ "Perceptual", PERCEPTUAL], [_ "Relative", RELATIVE], [_ "Saturation", SATURATION], [_ "Absolute", ABSOLUTE] ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ [_ "Point", POINT], [_ "Line", LINE], [_ "Spline", SPLINE], [_ "Bar", BAR] ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ [_ "YYYY", YYYY], [_ "XYYY", XYXY], [_ "XYXY", XYXY] ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [["format", Plot_format.XYYY]] value { } ================================================ FILE: share/nip2/compat/7.14/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Convert to") (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Tag as") (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Colour Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum Whitepoints (_ "Old whitepoint") "D65"; new_white = Option_enum Whitepoints (_ "New whitepoint") "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum Render_intent.names (_ "Render intent") (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; _result = map_unary chart x { chart in = measure 0 0 in.width in.height (to_real pacross) (to_real pdown) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.14/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_convf, !separable && type == 1 = im_convsepf, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 5) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height + 1) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_greyc_item = class Menupullright "_GREYCstoration" "noise-removing filter" { Denoise_item = class Menuaction "Denoise" "Noise-removing filter" { action x = class _result { _vislevel = 3; iterations = Scale "Iterations" 1 5 1; amplitude = Scale "Amplitude" 1 100 40; sharpness = Scale "Sharpness" 0 3 0.9; anisotropy = Scale "Anisotropy" 0 1 0.15; alpha = Scale "Noise scale" 0 5 0.6; sigma = Scale "Geometry regularity" 0 2 1.1; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) x; } } Enlarge_item = class Menuaction "Enlarge" "Enlarge image" { action x = class _result { _vislevel = 3; scale = Scale "Enlarge" 1 10 3; iterations = Scale "Iterations" 1 5 3; amplitude = Scale "Amplitude" 1 100 20; sharpness = Scale "Sharpness" 0 3 0.2; anisotropy = Scale "Anisotropy" 0 1 0.9; alpha = Scale "Noise scale" 0 5 0.1; sigma = Scale "Geometry regularity" 0 2 1.5; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) (resize (to_real scale) (to_real scale) Interpolate.BILINEAR x); } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ [_ "Normal", NORMAL], [_ "If Lighter", IFLIGHTER], [_ "If Darker", IFDARKER], [_ "Multiply", MULTIPLY], [_ "Add", ADD], [_ "Subtract", SUBTRACT], [_ "Screen", SCREEN], [_ "Burn", BURN], [_ "Soft Light", SOFTLIGHT], [_ "Hard Light", HARDLIGHT], [_ "Lighten", LIGHTEN], [_ "Darken", DARKEN], [_ "Dodge", DODGE], [_ "Reflect", REFLECT], [_ "Freeze", FREEZE], [_ "Bitwise OR", OR], [_ "Bitwise AND", AND] ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum names "Blend mode" "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } Filter_draw_line_item = class Menuaction "Draw _Line" "draw a line using an arrow as a guide" { } ================================================ FILE: share/nip2/compat/7.14/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "Histogram" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type field to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } } Hist_map_item = class Menuaction "_Map Histogram" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Cumulativise Histogram" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate Histogram" "find point-to-point differences (inverse of Cumulativise)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise Histogram" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_match_item = class Menuaction "Ma_tch Histogram" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); // extract that area area = extract_area (re p' + displace.value) (im p' - width.value / 2 + vdisplace.value) (re pv) width.value im'; // squish vertically to get an average area' = resize 1 (1 / width.value) Interpolate.BILINEAR area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = area { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); // extract that area area = extract_area (re p' + displace.value) (im p' - width.value / 2 + vdisplace.value) (re pv) width.value im'; } } } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; format = Option_enum Plot_format.names "Format" "YYYY"; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; image x = image (extract_arrow x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } extract_arrow arrow = extract_area (re p') (im p') (re pv) 1 im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } } } } ================================================ FILE: share/nip2/compat/7.14/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum Image_type.type_names "Image type" "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum field_names (_ "Field") name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum Image_type.type_names "Image type" (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; _result = map_unary process x { process image = rotate angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = map_unary straighten x { straighten arrow = rotate angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { _interp = Option_enum Interpolate.names "Interpolation" "Bilinear"; Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = _interp; _result = map_unary process x { process image = resize xfactor yfactor interp.value_thing image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; interp = _interp; _result = map_unary process x { process image = resize fac fac interp.value_thing image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = max_pair xfac yfac; min_factor = min_pair xfac yfac; fac = [max_factor, min_factor, xfac, yfac]?which; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.BILINEAR; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldr1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 2; ssize = Expression "Step size" 2; _result = Image (colour_transform_to (get_type start) im) { base = colour_transform_to Image_type.LAB start; step = to_real ssize; offset = to_real nstep * step; range c = [c - offset, c - offset + step ... c + offset]; mk_patch b a = image_new 150 150 3 Image_format.FLOAT Image_coding.NOCODING Image_type.LAB (Vector [base?0, a, b]) 0 0; mk_strip b = map (mk_patch b) (range base?1); mk_grid = map mk_strip (range base?2); im = imagearray_assemble 15 15 mk_grid; } } } } ================================================ FILE: share/nip2/compat/7.14/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.14 start_DATA = \ Math.def \ Image.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ Preferences.ws \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.14/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_And" "bitwise and of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_Or" "bitwise or of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "E_xclusive Or" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_Not" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Band_or_item = class Menuaction "Band O_r" "or the bands of an image together" { action im = map_unary (foldr1 bitwise_or @ bandsplit) im; } Band_and_item = class Menuaction "Band A_nd" "and the bands of an image together" { action im = map_unary (foldr1 bitwise_and @ bandsplit) im; } } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.14/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 (converse join_tb) (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 (converse join_lr) (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 (converse join_tb) (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 (converse join_lr) (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.14/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list (or group) of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.14/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/7.14/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linear input", "Fit intercept from chart greyscale", "Linearize input from chart greyscale" ] 2; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure 0 0 image.width image.height 6 4 image.value; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix [[0, 0], [1, 1]], mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix (map2 cons _true_grey_Y' _camera_grey') { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map the original image through the lineariser to // get linear 0-1 RGB image _image' = hist_map linearising_lut.value image.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ cast_float @ recomb M) _image'; // measure again and compute dE76 _camera'' = im_measure _result.value 0 0 image.width image.height 6 4; _dEs = map abs_vec (_camera'' - _true_Lab).value; final_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; // map the original image through the lineariser to get // linear 0-1 RGB image image' = hist_map calib.linearising_lut image; // convert linear RGB camera to Lab result = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M image')); } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize xfactor yfactor 1 scale_im; _offset_im = resize xfactor yfactor 1 offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.14/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.14/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0, 1, 2 or 3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, "colour_space"]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide or VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down a bit. */ check_args x = error message, badargs != [] || badalls != [] = check_args x.super, x.super != [] = x { argcheck = x._check_args; allcheck = x._check_all; // join two strings up with a separator string join_sep j a b = a ++ j ++ b; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad arguments to " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "usage" ++ "\n" ++ indent ++ usage ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make usage note usage = x.name ++ " " ++ foldr (join_sep " ") [] (map (extract 1) argcheck); // make arg type notes arg_types = foldr (join_sep "\n") [] (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = foldr (join_sep "\n") [] all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/7.14/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The innermost list objects become Group()'d. */ to_group x = Group x, is_list x && !contains_Group x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = (ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, im_8216 @ im_mono2sRGB], [B_W, GREY16, im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, im_8216], [RGB, GREY16, im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, im_8216], [sRGB, GREY16, im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* Given a list of things, try to make them all the same size. Don't change * the format. Don't touch non-image things. */ size_alike l = map enlarge l { max_width = foldr (test_prop has_width get_width) 0 l; max_height = foldr (test_prop has_height get_height) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); enlarge x = embed 0 0 0 max_width max_height x, has_width x = x; } /* Given a list of things, look for 1 band objects and bump them to to n - * band objects, where n is the maximum number of bands. Don't change the * format. Don't touch non-image things. */ bands_alike l = map upband l { max_bands = foldr (test_prop has_bands get_bands) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); upband x = bandjoin (replicate max_bands x), has_bands x && get_bands x == 1 = x; } /* Given a list of things, try to match the formats. Don't touch non-image * objects. */ formats_alike l = map upformat l { max_format = foldr (test_prop has_format get_format) Image_format.UCHAR l; test_prop has get x best = best, !has x = max_pair best (get x); upformat x = clip2fmt max_format x, has_format x = x; } /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = foldl1 fn l { fn a b = a ++ path_separator ++ b; } /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 == 2 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.14/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black (to_real w) (to_real h) (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = Matrix_con mask_g_sum 0 [mask_g_line] { mask_g = im_gauss_imask (radius / 3) 0.2; mask_g_line = mask_g.value?(mask_g.height / 2); mask_g_sum = sum mask_g_line; } /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } ================================================ FILE: share/nip2/compat/7.14/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height that * is extracted to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount color" false; ls = Expression "Lower mount section bigger by (cm)" 0; colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 6; control_selection mask im no = (if mask then im * 1.2 else im * 1), no == 0 = (if mask then im * 0.8 else im * 1), no == 1 = (if mask then 0 else im), no == 2 = (if mask then 255 else im), no == 3 = (if mask then im else 0), no == 4 = (if mask then im else 255), no == 5 = mask; Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = a.image; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = ((pt_list.value)?0).image; } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize f1 f2 1 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize f1 1 1 b1 {b1 = resize 1 f2 1 b;} } } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.14/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} } ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; } ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); } ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = line.image; //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = (pt_l?0).image; im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; } ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; } ================================================ FILE: share/nip2/compat/7.14/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn (tl l), fn (hd l) = l; /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); sum = foldr1 add; product = foldr1 multiply; /* Search a list for an element, returning it's index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st a) x { a:x = l; } } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.14/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = error ("get_format: unable to get format from " ++ print x); has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = error ("get_bits: unable to get bits from " ++ print x); has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = error ("get_bands: unable to get bands from " ++ print x); has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = error ("get_coding: unable to get coding from " ++ print x); has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = error ("get_xres: unable to get xres from " ++ print x); has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = error ("get_yres: unable to get yres from " ++ print x); has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = error ("get_xoffset: unable to get xoffset from " ++ print x); has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = error ("get_yoffset: unable to get yoffset from " ++ print x); has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = error ("get_image: unable to get image from " ++ print x); has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = error ("get_number: unable to get number from " ++ print x); has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = error ("get_real: unable to get real from " ++ print x); has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = error ("get_width: unable to get width from " ++ print x); has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = error ("get_height: unable to get height from " ++ print x); has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = error ("get_left: unable to get left from " ++ print x); has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = error ("get_top: unable to get top from " ++ print x); // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.14/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error "unimplemented vector operation" { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend") { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = x, is_image x = x.value, is_Image x = black + x { black = im_black target_width target_height target_bands; } [then_image, else_image] = map (clip2fmt target_format @ to_image) [then_part, else_part]; [c, t, e] = size_alike [cond, then_image, else_image]; blend_result_image = image_set_type target_type (im_blend c t e); } } insert x y small big = oo_binary_function insert_op small big, is_class small = oo_binary'_function insert_op small big, is_class big = im_insert big' small' (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; [small', big'] = bands_alike [small, big]; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big' small' (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; [small', big'] = bands_alike [small, big]; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (_ "bad arguments to " ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_similarity image (cos angle) (sin angle) 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" rotate Operator_type.COMPOUND_REWRAP false; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join a b { join_lr_op = Operator "join_lr" join Operator_type.COMPOUND_REWRAP false; join a b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr"); join_im a b = insert (get_width b) 0 a b; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join a b { join_tb_op = Operator "join_tb" join Operator_type.COMPOUND_REWRAP false; join a b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb"); join_im a b = insert 0 (get_height b) a b; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { image = (unsigned char) im_mask2vips (Matrix m2); m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsepf image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx x = oo_unary_function greyc_op x, is_class x = greyc_im x, is_image x = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") { greyc_op = Operator "greyc" (greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im x = im_greyc x iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx mask x = oo_binary_function greyc_mask_op mask x, is_class mask = oo_binary'_function greyc_mask_op mask x, is_class x = greyc_im mask x, is_image mask && is_image x = error (_ "bad arguments" ++ " (" ++ print mask ++ ", " ++ print x ++ ") " ++ "to " ++ "greyc") { greyc_mask_op = Operator "greyc_mask" (greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im mask x = im_greyc_mask x mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize xfac yfac interp image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, nearest neighbour // can't really do this right ... upscale by integer part, then // bilinear to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by any factor, nearest neighbour // can't really do this right ... downscale by integer part, // then bilinear to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, bilinear = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 && interp == Interpolate.BILINEAR // downscale by any factor, bilinear // block shrink by integer factor, then bilinear resample to // exact = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.BILINEAR = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate.names.lookup 1 0 interp ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // binlinear resize scale xfac yfac im = im_affine im xfac 0 0 yfac 0 0 0 0 (rint (get_width im * xfac)) (rint (get_height im * yfac)); } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } flood_blob x y v image = oo_unary_function flood_blob_op image, is_class image = im_flood_blob_copy image (to_real x) (to_real y) v, is_image image = error (_ "bad arguments to " ++ "flood_blob") { flood_blob_op = Operator "flood_blob" (flood_blob x y v) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = [], any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == []; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Image (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; } ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } ================================================ FILE: share/nip2/compat/7.14/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] { // need ite as a true trinary ite a b c = if a then b else c; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = [], any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == []; } } map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } oo_unary_table op = [ [map_unary op.fn this, true] ] { // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = [], any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == []; } } map_unary fn a = map_nary (list_1ary fn) [a]; } } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ]; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ]; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ]; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ]; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.value_name colour.expr { _vislevel = 3; space = Option_enum Image_type.colour_spaces "Colour space" default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } Option_enum enum caption value_name = class Option caption enum.names (index (equal value_name) enum.names) { // corresponding thing value_thing = enum.get_thing value_name; Option_edit caption labels value = this.Option_enum enum caption (enum.names ? value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* present col x: is there an x in column col */ present col x = member (map (extract col) value) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (map (extract from) value); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = map (extract 0) value; things = map (extract 1) value; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; LUMINANCE = 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; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $LUMINANCE => LUMINANCE, $XRAY => XRAY, $IR => IR, $YUV => YUV, $RED_ONLY => RED_ONLY, $GREEN_ONLY => GREEN_ONLY, $BLUE_ONLY => BLUE_ONLY, $POWER_SPECTRUM => POWER_SPECTRUM, $HISTOGRAM => HISTOGRAM, $LUT => LUT, $XYZ => XYZ, $LAB => LAB, $CMC => CMC, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16 ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], // image ++ image is slightly different ... we want to // sizealike, but we must not bandalike [wrap (op.fn (get_image resized?0) (get_image resized?1)), has_image x && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], // arithmetic and reational binops between image resize // and band_alike images to match [wrap (op.fn (get_image rebanded?0) (get_image rebanded?1)), has_image x && (op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL)], // other op types don't resize [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. "Image v == // 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = []; to_image x = x, is_image x = x.value, is_Image x = clip2fmt target_format im, target_format != [] = im { im = im_black width height target_bands + x; } [if_size, then_size, else_size] = size_alike (value : formats_alike (map to_image x)); ite_result_image = image_set_type target_type (if if_size then then_size else else_size); resized = size_alike [this, x]; rebanded = bands_alike resized; } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; /* Table to map interpol numbers to descriptive strings */ names = Enum [ [_ "Nearest neighbour", NEAREST_NEIGHBOUR], [_ "Bilinear", BILINEAR], [_ "Bicubic", BICUBIC] ]; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ [_ "Perceptual", PERCEPTUAL], [_ "Relative", RELATIVE], [_ "Saturation", SATURATION], [_ "Absolute", ABSOLUTE] ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ [_ "Point", POINT], [_ "Line", LINE], [_ "Spline", SPLINE], [_ "Bar", BAR] ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ [_ "YYYY", YYYY], [_ "XYYY", XYXY], [_ "XYXY", XYXY] ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } ================================================ FILE: share/nip2/compat/7.16/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Convert to") (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Tag as") (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Colour Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum Whitepoints (_ "Old whitepoint") "D65"; new_white = Option_enum Whitepoints (_ "New whitepoint") "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum Render_intent.names (_ "Render intent") (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; _result = map_unary chart x { chart in = measure 0 0 in.width in.height (to_real pacross) (to_real pdown) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.16/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_convf, !separable && type == 1 = im_convsepf, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 5) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height + 1) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_greyc_item = class Menupullright "_GREYCstoration" "noise-removing filter" { Denoise_item = class Menuaction "Denoise" "Noise-removing filter" { action x = class _result { _vislevel = 3; iterations = Scale "Iterations" 1 5 1; amplitude = Scale "Amplitude" 1 100 40; sharpness = Scale "Sharpness" 0 3 0.9; anisotropy = Scale "Anisotropy" 0 1 0.15; alpha = Scale "Noise scale" 0 5 0.6; sigma = Scale "Geometry regularity" 0 2 1.1; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) x; } } Enlarge_item = class Menuaction "Enlarge" "Enlarge image" { action x = class _result { _vislevel = 3; scale = Scale "Enlarge" 1 10 3; iterations = Scale "Iterations" 1 5 3; amplitude = Scale "Amplitude" 1 100 20; sharpness = Scale "Sharpness" 0 3 0.2; anisotropy = Scale "Anisotropy" 0 1 0.9; alpha = Scale "Noise scale" 0 5 0.1; sigma = Scale "Geometry regularity" 0 2 1.5; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) (resize (to_real scale) (to_real scale) Interpolate.BILINEAR x); } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ [_ "Normal", NORMAL], [_ "If Lighter", IFLIGHTER], [_ "If Darker", IFDARKER], [_ "Multiply", MULTIPLY], [_ "Add", ADD], [_ "Subtract", SUBTRACT], [_ "Screen", SCREEN], [_ "Burn", BURN], [_ "Soft Light", SOFTLIGHT], [_ "Hard Light", HARDLIGHT], [_ "Lighten", LIGHTEN], [_ "Darken", DARKEN], [_ "Dodge", DODGE], [_ "Reflect", REFLECT], [_ "Freeze", FREEZE], [_ "Bitwise OR", OR], [_ "Bitwise AND", AND] ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum names "Blend mode" "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ] ++ super._check_args; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/7.16/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "Histogram" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type field to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } } Hist_map_item = class Menuaction "_Map Histogram" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Cumulativise Histogram" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate Histogram" "find point-to-point differences (inverse of Cumulativise)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise Histogram" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_match_item = class Menuaction "Ma_tch Histogram" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); // extract that area area = extract_area (re p' + displace.value) (im p' - width.value / 2 + vdisplace.value) (re pv) width.value im'; // squish vertically to get an average area' = resize 1 (1 / width.value) Interpolate.BILINEAR area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = area { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); // extract that area area = extract_area (re p' + displace.value) (im p' - width.value / 2 + vdisplace.value) (re pv) width.value im'; } } } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; format = Option_enum Plot_format.names "Format" "YYYY"; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; image x = image (extract_arrow x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } extract_arrow arrow = extract_area (re p') (im p') (re pv) 1 im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } } } } ================================================ FILE: share/nip2/compat/7.16/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum Image_type.type_names "Image type" "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum field_names (_ "Field") name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum Image_type.type_names "Image type" (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 32; tile_height = Number "Tile height" 32; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; _result = map_unary process x { process image = rotate angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = map_unary straighten x { straighten arrow = rotate angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { _interp = Option_enum Interpolate.names "Interpolation" "Bilinear"; Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = _interp; _result = map_unary process x { process image = resize xfactor yfactor interp.value_thing image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; interp = _interp; _result = map_unary process x { process image = resize fac fac interp.value_thing image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = max_pair xfac yfac; min_factor = min_pair xfac yfac; fac = [max_factor, min_factor, xfac, yfac]?which; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = _interp; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize fac fac interp.value_thing x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.BILINEAR; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 2; ssize = Expression "Step size" 2; _result = Image (colour_transform_to (get_type start) im) { base = colour_transform_to Image_type.LAB start; step = to_real ssize; offset = to_real nstep * step; range c = [c - offset, c - offset + step ... c + offset]; mk_patch b a = image_new 8 8 3 Image_format.FLOAT Image_coding.NOCODING Image_type.LAB (Vector [base?0, a, b]) 0 0; mk_strip b = map (mk_patch b) (range base?1); mk_grid = map mk_strip (range base?2); im = imagearray_assemble 1 1 mk_grid; } } } } ================================================ FILE: share/nip2/compat/7.16/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.16 start_DATA = \ Math.def \ Image.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ Preferences.ws \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.16/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_And" "bitwise and of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_Or" "bitwise or of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "E_xclusive Or" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_Not" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Band_or_item = class Menuaction "Band O_r" "or the bands of an image together" { action im = map_unary (foldr1 bitwise_or @ bandsplit) im; } Band_and_item = class Menuaction "Band A_nd" "and the bands of an image together" { action im = map_unary (foldr1 bitwise_and @ bandsplit) im; } } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 2; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.16/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ] ++ super._check_args; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ] ++ super._check_args; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.16/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list (or group) of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.16/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/7.16/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linear input", "Fit intercept from chart greyscale", "Linearize input from chart greyscale" ] 2; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure 0 0 image.width image.height 6 4 image.value; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix [[0, 0], [1, 1]], mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix (map2 cons _true_grey_Y' _camera_grey') { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map the original image through the lineariser to // get linear 0-1 RGB image _image' = hist_map linearising_lut.value image.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ cast_float @ recomb M) _image'; // measure again and compute dE76 _camera'' = im_measure _result.value 0 0 image.width image.height 6 4; _dEs = map abs_vec (_camera'' - _true_Lab).value; final_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; // map the original image through the lineariser to get // linear 0-1 RGB image image' = hist_map calib.linearising_lut image; // convert linear RGB camera to Lab result = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M image')); } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b ++ super._check_args; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b ++ super._check_args; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d ++ super._check_args; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d ++ super._check_args; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize xfactor yfactor 1 scale_im; _offset_im = resize xfactor yfactor 1 offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.16/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.16/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down a bit. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/7.16/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The innermost list objects become Group()'d. */ to_group x = Group x, is_list x && !contains_Group x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = (ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* Given a list of things, try to make them all the same size. Don't change * the format. Don't touch non-image things. */ size_alike l = map enlarge l { max_width = foldr (test_prop has_width get_width) 0 l; max_height = foldr (test_prop has_height get_height) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); enlarge x = embed 0 0 0 max_width max_height x, has_width x = x; } /* Given a list of things, look for 1 band objects and bump them to to n - * band objects, where n is the maximum number of bands. Don't change the * format. Don't touch non-image things. */ bands_alike l = map upband l { max_bands = foldr (test_prop has_bands get_bands) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); upband x = bandjoin (replicate max_bands x), has_bands x && get_bands x == 1 = x; } /* Given a list of things, try to match the formats. Don't touch non-image * objects. */ formats_alike l = map upformat l { max_format = foldr (test_prop has_format get_format) Image_format.UCHAR l; test_prop has get x best = best, !has x = max_pair best (get x); upformat x = clip2fmt max_format x, has_format x = x; } /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 == 2 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.16/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black (to_real w) (to_real h) (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = Matrix_con mask_g_sum 0 [mask_g_line] { mask_g = im_gauss_imask (radius / 3) 0.2; mask_g_line = mask_g.value?(mask_g.height / 2); mask_g_sum = sum mask_g_line; } /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } ================================================ FILE: share/nip2/compat/7.16/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ] ++ super._check_all; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ] ++ super._check_all; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height that * is extracted to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ] ++ super._check_all; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = class { scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; } _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = class { apply = Toggle "Apply mount color" false; ls = Expression "Lower mount section bigger by (cm)" 0; colour = Colour _type [0, 0, 0]; _los = ls.expr * ppcm.expr; } _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize _sf _sf Interpolate.BILINEAR a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 6; control_selection mask im no = (if mask then im * 1.2 else im * 1), no == 0 = (if mask then im * 0.8 else im * 1), no == 1 = (if mask then 0 else im), no == 2 = (if mask then 255 else im), no == 3 = (if mask then im else 0), no == 4 = (if mask then im else 255), no == 5 = mask; Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = a.image; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = ((pt_list.value)?0).image; } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize f1 f2 1 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize f1 1 1 b1 {b1 = resize 1 f2 1 b;} } } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.16/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} } ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; } ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); } ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = line.image; //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = (pt_l?0).image; im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; } ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; } ================================================ FILE: share/nip2/compat/7.16/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn (tl l), fn (hd l) = l; /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); sum = foldr1 add; product = foldr1 multiply; /* Search a list for an element, returning it's index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st a) x { a:x = l; } } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.16/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = error ("get_format: unable to get format from " ++ print x); has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = error ("get_bits: unable to get bits from " ++ print x); has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = error ("get_bands: unable to get bands from " ++ print x); has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = error ("get_coding: unable to get coding from " ++ print x); has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = error ("get_xres: unable to get xres from " ++ print x); has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = error ("get_yres: unable to get yres from " ++ print x); has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = error ("get_xoffset: unable to get xoffset from " ++ print x); has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = error ("get_yoffset: unable to get yoffset from " ++ print x); has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = error ("get_image: unable to get image from " ++ print x); has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = error ("get_number: unable to get number from " ++ print x); has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = error ("get_real: unable to get real from " ++ print x); has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = error ("get_width: unable to get width from " ++ print x); has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = error ("get_height: unable to get height from " ++ print x); has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = error ("get_left: unable to get left from " ++ print x); has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = error ("get_top: unable to get top from " ++ print x); // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.16/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error "unimplemented vector operation" { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend") { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = x, is_image x = x.value, is_Image x = black + x { black = im_black target_width target_height target_bands; } [then_image, else_image] = map (clip2fmt target_format @ to_image) [then_part, else_part]; [c, t, e] = size_alike [cond, then_image, else_image]; blend_result_image = image_set_type target_type (im_blend c t e); } } insert x y small big = oo_binary_function insert_op small big, is_class small = oo_binary'_function insert_op small big, is_class big = im_insert big' small' (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; [small', big'] = (formats_alike @ bands_alike) [small, big]; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big' small' (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; [small', big'] = (formats_alike @ bands_alike) [small, big]; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (_ "bad arguments to " ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_similarity image (cos angle) (sin angle) 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" rotate Operator_type.COMPOUND_REWRAP false; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join a b { join_lr_op = Operator "join_lr" join Operator_type.COMPOUND_REWRAP false; join a b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr"); join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join a b { join_tb_op = Operator "join_tb" join Operator_type.COMPOUND_REWRAP false; join a b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb"); join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { image = (unsigned char) im_mask2vips (Matrix m2); m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsepf image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx x = oo_unary_function greyc_op x, is_class x = greyc_im x, is_image x = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") { greyc_op = Operator "greyc" (greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im x = im_greyc x iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx mask x = oo_binary_function greyc_mask_op mask x, is_class mask = oo_binary'_function greyc_mask_op mask x, is_class x = greyc_im mask x, is_image mask && is_image x = error (_ "bad arguments" ++ " (" ++ print mask ++ ", " ++ print x ++ ") " ++ "to " ++ "greyc") { greyc_mask_op = Operator "greyc_mask" (greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im mask x = im_greyc_mask x mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize xfac yfac interp image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, nearest neighbour // can't really do this right ... upscale by integer part, then // bilinear to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // downscale by any factor, nearest neighbour // can't really do this right ... downscale by integer part, // then bilinear to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.NEAREST_NEIGHBOUR // upscale by any factor, bilinear = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 && interp == Interpolate.BILINEAR // downscale by any factor, bilinear // block shrink by integer factor, then bilinear resample to // exact = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && interp == Interpolate.BILINEAR = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate.names.lookup 1 0 interp ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // binlinear resize scale xfac yfac im = im_affine im xfac 0 0 yfac 0 0 0 0 (rint (get_width im * xfac)) (rint (get_height im * yfac)); } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } flood_blob x y v image = oo_unary_function flood_blob_op image, is_class image = im_flood_blob_copy image (to_real x) (to_real y) v, is_image image = error (_ "bad arguments to " ++ "flood_blob") { flood_blob_op = Operator "flood_blob" (flood_blob x y v) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Image (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; } ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } ================================================ FILE: share/nip2/compat/7.16/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ] ++ super._check_args; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ] ++ super._check_args; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ] ++ super._check_args; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ] ++ super._check_args; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ] ++ super._check_args; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ] ++ super._check_args; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ] ++ super._check_args; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ] ++ super._check_args; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ] ++ super._check_args; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ] ++ super._check_args; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ] ++ super._check_args; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ] ++ super._check_args; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ] ++ super._check_args; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ] ++ super._check_args; _check_all = [ [is_list_len 3 value, "len value == 3"] ] ++ super._check_all; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.value_name colour.expr { _vislevel = 3; space = Option_enum Image_type.colour_spaces "Colour space" default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ] ++ super._check_args; _check_all = [ [from < to, "from < to"] ] ++ super._check_all; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ] ++ super._check_args; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ] ++ super._check_args; } Option_enum enum caption value_name = class Option caption enum.names (index (equal value_name) enum.names) { // corresponding thing value_thing = enum.get_thing value_name; Option_edit caption labels value = this.Option_enum enum caption (enum.names ? value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ] ++ super._check_args; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ] ++ super._check_args; /* present col x: is there an x in column col */ present col x = member (map (extract col) value) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (map (extract from) value); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] ++ super._check_args { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = map (extract 0) value; things = map (extract 1) value; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; LUMINANCE = 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; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $LUMINANCE => LUMINANCE, $XRAY => XRAY, $IR => IR, $YUV => YUV, $RED_ONLY => RED_ONLY, $GREEN_ONLY => GREEN_ONLY, $BLUE_ONLY => BLUE_ONLY, $POWER_SPECTRUM => POWER_SPECTRUM, $HISTOGRAM => HISTOGRAM, $LUT => LUT, $XYZ => XYZ, $LAB => LAB, $CMC => CMC, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16 ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ] ++ super._check_args; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], // image ++ image is slightly different ... we want to // sizealike, but we must not bandalike [wrap (op.fn (get_image resized?0) (get_image resized?1)), has_image x && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], // arithmetic and reational binops between image resize // and band_alike images to match [wrap (op.fn (get_image rebanded?0) (get_image rebanded?1)), has_image x && (op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL)], // other op types don't resize [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. "Image v == // 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = []; to_image x = x, is_image x = x.value, is_Image x = clip2fmt target_format im, target_format != [] = im { im = im_black width height target_bands + x; } [if_size, then_size, else_size] = size_alike (value : formats_alike (map to_image x)); ite_result_image = image_set_type target_type (if if_size then then_size else else_size); resized = size_alike [this, x]; rebanded = bands_alike resized; } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ] ++ super._check_args; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ] ++ super._check_args; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ] ++ super._check_args; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; /* Table to map interpol numbers to descriptive strings */ names = Enum [ [_ "Nearest neighbour", NEAREST_NEIGHBOUR], [_ "Bilinear", BILINEAR], [_ "Bicubic", BICUBIC] ]; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ [_ "Perceptual", PERCEPTUAL], [_ "Relative", RELATIVE], [_ "Saturation", SATURATION], [_ "Absolute", ABSOLUTE] ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ [_ "Point", POINT], [_ "Line", LINE], [_ "Spline", SPLINE], [_ "Bar", BAR] ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ [_ "YYYY", YYYY], [_ "XYYY", XYXY], [_ "XYXY", XYXY] ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/7.24/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Convert to") (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum spaces (_ "Tag as") (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Colour Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum Whitepoints (_ "Old whitepoint") "D65"; new_white = Option_enum Whitepoints (_ "New whitepoint") "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum Render_intent.names (_ "Render intent") (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; _result = map_unary chart x { chart in = measure 0 0 in.width in.height (to_real pacross) (to_real pdown) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.24/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees"] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } sep3 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_greyc_item = class Menupullright "_GREYCstoration" "noise-removing filter" { Denoise_item = class Menuaction "Denoise" "Noise-removing filter" { action x = class _result { _vislevel = 3; iterations = Scale "Iterations" 1 5 1; amplitude = Scale "Amplitude" 1 100 40; sharpness = Scale "Sharpness" 0 3 0.9; anisotropy = Scale "Anisotropy" 0 1 0.15; alpha = Scale "Noise scale" 0 5 0.6; sigma = Scale "Geometry regularity" 0 2 1.1; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) x; } } Enlarge_item = class Menuaction "Enlarge" "Enlarge image" { action x = class _result { _vislevel = 3; scale = Scale "Enlarge" 1 10 3; iterations = Scale "Iterations" 1 5 3; amplitude = Scale "Amplitude" 1 100 20; sharpness = Scale "Sharpness" 0 3 0.2; anisotropy = Scale "Anisotropy" 0 1 0.9; alpha = Scale "Noise scale" 0 5 0.1; sigma = Scale "Geometry regularity" 0 2 1.5; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) (resize Interpolate_bilinear (to_real scale) (to_real scale) x); } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ [_ "Normal", NORMAL], [_ "If Lighter", IFLIGHTER], [_ "If Darker", IFDARKER], [_ "Multiply", MULTIPLY], [_ "Add", ADD], [_ "Subtract", SUBTRACT], [_ "Screen", SCREEN], [_ "Burn", BURN], [_ "Soft Light", SOFTLIGHT], [_ "Hard Light", HARDLIGHT], [_ "Lighten", LIGHTEN], [_ "Darken", DARKEN], [_ "Dodge", DODGE], [_ "Reflect", REFLECT], [_ "Freeze", FREEZE], [_ "Bitwise OR", OR], [_ "Bitwise AND", AND] ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum names "Blend mode" "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/7.24/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "Histogram" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Cumulativise" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Cumulativise)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Interpolate_bilinear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; format = Option_enum Plot_format.names "Format" "YYYY"; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/7.24/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum Image_type.type_names "Image type" "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum field_names (_ "Field") name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum Image_type.type_names "Image type" (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp h v image, aspect = resize interp fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize interp fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/7.24/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.24 start_DATA = \ Math.def \ Image.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.24/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_And" "bitwise and of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_Or" "bitwise or of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "E_xclusive Or" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_Not" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Band_or_item = class Menuaction "Band O_r" "or the bands of an image together" { action im = map_unary (foldr1 bitwise_or @ bandsplit) im; } Band_and_item = class Menuaction "Band A_nd" "and the bands of an image together" { action im = map_unary (foldr1 bitwise_and @ bandsplit) im; } } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.24/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.24/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list (or group) of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.24/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linear input", "Fit intercept from chart greyscale", "Linearize input from chart greyscale" ] 2; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure 0 0 image.width image.height 6 4 image.value; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix [[0, 0], [1, 1]], mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix (map2 cons _true_grey_Y' _camera_grey') { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map the original image through the lineariser to // get linear 0-1 RGB image _image' = hist_map linearising_lut.value image.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ cast_float @ recomb M) _image'; // measure again and compute dE76 _camera'' = im_measure _result.value 0 0 image.width image.height 6 4; _dEs = map abs_vec (_camera'' - _true_Lab).value; final_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; // map the original image through the lineariser to get // linear 0-1 RGB image image' = hist_map calib.linearising_lut image; // convert linear RGB camera to Lab result = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M image')); } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum Plot_style.names "Style" "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Interpolate_bilinear xfactor yfactor scale_im; _offset_im = resize Interpolate_bilinear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.24/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.24/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/7.24/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The innermost list objects become Group()'d. */ to_group x = Group x, is_list x && !contains_Group x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = (ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* Given a list of things, try to make them all the same size. Don't change * the format. Don't touch non-image things. */ size_alike l = map enlarge l { max_width = foldr (test_prop has_width get_width) 0 l; max_height = foldr (test_prop has_height get_height) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); enlarge x = embed 0 0 0 max_width max_height x, has_width x = x; } /* Given a list of things, look for 1 band objects and bump them to to n - * band objects, where n is the maximum number of bands. Don't change the * format. Don't touch non-image things. */ bands_alike l = map upband l { max_bands = foldr (test_prop has_bands get_bands) 0 l; test_prop has get x best = best, !has x = max_pair best (get x); upband x = bandjoin (replicate max_bands x), has_bands x && get_bands x == 1 = x; } /* Given a list of things, try to match the formats. Don't touch non-image * objects. */ formats_alike l = map upformat l { max_format = foldr (test_prop has_format get_format) Image_format.UCHAR l; test_prop has get x best = best, !has x = max_pair best (get x); upformat x = clip2fmt max_format x, has_format x = x; } /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 == 2 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.24/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } ================================================ FILE: share/nip2/compat/7.24/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = (if mask then im * 1.2 else im * 1), no == 0 = (if mask then im * 0.8 else im * 1), no == 1 = (if mask then 0 else im), no == 2 = (if mask then 255 else im), no == 3 = (if mask then im else 0), no == 4 = (if mask then im else 255), no == 5 = mask; Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Interpolate_bilinear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Interpolate_bilinear f1 1 b1 {b1 = resize Interpolate_bilinear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.24/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/7.24/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st a) x { a:x = l; } } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.24/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = error ("get_format: unable to get format from " ++ print x); has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = error ("get_bits: unable to get bits from " ++ print x); has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = error ("get_bands: unable to get bands from " ++ print x); has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = error ("get_coding: unable to get coding from " ++ print x); has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = error ("get_xres: unable to get xres from " ++ print x); has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = error ("get_yres: unable to get yres from " ++ print x); has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = error ("get_xoffset: unable to get xoffset from " ++ print x); has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = error ("get_yoffset: unable to get yoffset from " ++ print x); has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = error ("get_image: unable to get image from " ++ print x); has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = error ("get_number: unable to get number from " ++ print x); has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = error ("get_real: unable to get real from " ++ print x); has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = error ("get_width: unable to get width from " ++ print x); has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = error ("get_height: unable to get height from " ++ print x); has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = error ("get_left: unable to get left from " ++ print x); has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = error ("get_top: unable to get top from " ++ print x); // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.24/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend") { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; [c, t, e] = size_alike [cond, then_image, else_image]; blend_result_image = image_set_type target_type (im_blend c t e); } } insert x y small big = oo_binary_function insert_op small big, is_class small = oo_binary'_function insert_op small big, is_class big = im_insert big' small' (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; [small', big'] = (formats_alike @ bands_alike) [small, big]; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big' small' (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; [small', big'] = (formats_alike @ bands_alike) [small, big]; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (_ "bad arguments to " ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx x = oo_unary_function greyc_op x, is_class x = greyc_im x, is_image x = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") { greyc_op = Operator "greyc" (greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im x = im_greyc x iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx mask x = oo_binary_function greyc_mask_op mask x, is_class mask = oo_binary'_function greyc_mask_op mask x, is_class x = greyc_im mask x, is_image mask && is_image x = error (_ "bad arguments" ++ " (" ++ print mask ++ ", " ++ print x ++ ") " ++ "to " ++ "greyc") { greyc_mask_op = Operator "greyc_mask" (greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im mask x = im_greyc_mask x mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize interp xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; // is this interpolation nearest-neighbour? is_nn x = x.type == Interpolate_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor, nearest neighbour // upscale by integer part, then affine to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by any factor, nearest neighbour // downscale by integer part, then affine to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor with affine = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 // downscale by any factor, bilinear // block shrink by integer factor, then resample to // exact with affine = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate_type.descriptions?interp.type ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // resize scale xfac yfac im = im_affinei_all im interp.value xfac 0 0 yfac 0 0; } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } flood_blob x y v image = oo_unary_function flood_blob_op image, is_class image = im_flood_blob_copy image (to_real x) (to_real y) v, is_image image = error (_ "bad arguments to " ++ "flood_blob") { flood_blob_op = Operator "flood_blob" (flood_blob x y v) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; } ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } ================================================ FILE: share/nip2/compat/7.24/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.value_name colour.expr { _vislevel = 3; space = Option_enum Image_type.colour_spaces "Colour space" default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } Option_enum enum caption value_name = class Option caption enum.names (index (equal value_name) enum.names) { // corresponding thing value_thing = enum.get_thing value_name; Option_edit caption labels value = this.Option_enum enum caption (enum.names ? value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; LUMINANCE = 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; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $LUMINANCE => LUMINANCE, $XRAY => XRAY, $IR => IR, $YUV => YUV, $RED_ONLY => RED_ONLY, $GREEN_ONLY => GREEN_ONLY, $BLUE_ONLY => BLUE_ONLY, $POWER_SPECTRUM => POWER_SPECTRUM, $HISTOGRAM => HISTOGRAM, $LUT => LUT, $XYZ => XYZ, $LAB => LAB, $CMC => CMC, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16 ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], // image ++ image is slightly different ... we want to // sizealike, but we must not bandalike [wrap (op.fn (get_image resized?0) (get_image resized?1)), has_image x && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], // arithmetic and reational binops between image resize // and band_alike images to match [wrap (op.fn (get_image rebanded?0) (get_image rebanded?1)), has_image x && (op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL)], // other op types don't resize [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [if_size, then_size, else_size] = size_alike (value : formats_alike (map to_image x)); ite_result_image = image_set_type target_type (if if_size then then_size else else_size); resized = size_alike [this, x]; rebanded = bands_alike resized; } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ [_ "Perceptual", PERCEPTUAL], [_ "Relative", RELATIVE], [_ "Saturation", SATURATION], [_ "Absolute", ABSOLUTE] ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ [_ "Point", POINT], [_ "Line", LINE], [_ "Spline", SPLINE], [_ "Bar", BAR] ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ [_ "YYYY", YYYY], [_ "XYYY", XYXY], [_ "XYXY", XYXY] ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/7.26/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Colour Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; _result = map_unary chart x { chart in = measure 0 0 in.width in.height (to_real pacross) (to_real pdown) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.26/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } sep3 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_greyc_item = class Menupullright "_GREYCstoration" "noise-removing filter" { Denoise_item = class Menuaction "Denoise" "Noise-removing filter" { action x = class _result { _vislevel = 3; iterations = Scale "Iterations" 1 5 1; amplitude = Scale "Amplitude" 1 100 40; sharpness = Scale "Sharpness" 0 3 0.9; anisotropy = Scale "Anisotropy" 0 1 0.15; alpha = Scale "Noise scale" 0 5 0.6; sigma = Scale "Geometry regularity" 0 2 1.1; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) x; } } Enlarge_item = class Menuaction "Enlarge" "Enlarge image" { action x = class _result { _vislevel = 3; scale = Scale "Enlarge" 1 10 3; iterations = Scale "Iterations" 1 5 3; amplitude = Scale "Amplitude" 1 100 20; sharpness = Scale "Sharpness" 0 3 0.2; anisotropy = Scale "Anisotropy" 0 1 0.9; alpha = Scale "Noise scale" 0 5 0.1; sigma = Scale "Geometry regularity" 0 2 1.5; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) (resize Interpolate_bilinear (to_real scale) (to_real scale) x); } } } Filter_magick_item = class Menupullright "Magic_k" "various Image/Graphics Magick filters" { system command x = map_unary (system_image command) x; radius_widget = Scale "Radius" 1 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; print_colour triple = concat ["\"#", concat (map fmt triple), "\""] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; Background triple = class Colour "sRGB" triple { _flag = "-background " ++ print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Geometry_widget = class { _vislevel = 3; x = Expression "Y" 0; y = Expression "X" 0; hoffset = Expression "Horizontal offset" 10; voffset = Expression "Vertical offset" 10; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } Adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; radius = radius_widget; sigma = sigma_widget; command = magick_command (concat ["-adaptive-blur ", print radius.value, "x", print sigma.value]); _result = system command x; } } Adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; radius = radius_widget; sigma = sigma_widget; command = magick_command (concat ["-adaptive-sharpen ", print radius.value, "x", print sigma.value]); _result = system command x; } } Alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = alpha_widget; command = magick_command alpha._flag; _result = system command x; } } Annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = text_widget; font = Font_widget; geometry = Geometry_widget; gravity = gravity_widget; foreground = foreground_widget; antialias = antialias_widget; command = magick_command (join_sep " " [ font._flag, antialias._flag, gravity._flag, foreground._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\""]); _result = system command x; } } Swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = angle_widget; command = magick_command ("-swirl " ++ print angle.value); _result = system command x; } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/7.26/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "Histogram" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Interpolate_bilinear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/7.26/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp h v image, aspect = resize interp fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize interp fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; ink = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1.expr y1.expr x2.expr y2.expr ink.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; left = Expression "Left" 50; top = Expression "Top" 50; width = Expression "Width" 100; height = Expression "Height" 100; fill = Toggle "Fill" true; ink = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect left.expr top.expr width.expr height.expr fill.value ink.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; fill = Toggle "Fill" true; ink = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx.expr cy.expr r.expr fill.value ink.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; startx = Expression "Start x" 100; starty = Expression "Start y" 100; edge = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can ink = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area left top 1 1 im)); left = startx.expr; top = starty.expr; im = get_image x; } _result = map_unary flood x { flood im = draw_flood left top ink.expr im, edge == 0 = draw_flood_blob left top ink.expr im { left = startx.expr; top = starty.expr; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/7.26/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.26 start_DATA = \ Math.def \ Image.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.26/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_And" "bitwise and of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_Or" "bitwise or of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "E_xclusive Or" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_Not" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Band_or_item = class Menuaction "Band O_r" "or the bands of an image together" { action im = map_unary (foldr1 bitwise_or @ bandsplit) im; } Band_and_item = class Menuaction "Band A_nd" "and the bands of an image together" { action im = map_unary (foldr1 bitwise_and @ bandsplit) im; } } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.26/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = class _result { _vislevel = 3; s = Expression "Size" 5; _result = Matrix (identity_matrix s.expr); } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = class _result { _vislevel = 3; s = Expression "Start value" 0; t = Expression "Step by" 1; e = Expression "End value" 5; _result = Matrix (transpose [series]) { series = [to_real s, to_real t.. to_real e]; } } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = class _result { _vislevel = 3; s = Expression "Size" 5; _result = Matrix_con (s.expr ** 2) 0 square { line = take s.expr [1, 1 ..]; square = replicate s.expr line; } } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = class _result { _vislevel = 3; r = Expression "Radius" 5; _result = Matrix_con (sum circle) 0 circle { line = [-r.expr .. r.expr]; xes = replicate (2 * r.expr + 1) line; yes = transpose xes; circle = map2 (map2 pyth) xes yes { pyth a b = 1, (a**2 + b**2) ** 0.5 <= r.expr = 0; } } } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.26/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list (or group) of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.26/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linear input", "Fit intercept from chart greyscale", "Linearize input from chart greyscale" ] 2; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure 0 0 image.width image.height 6 4 image.value; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix [[0, 0], [1, 1]], mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix (map2 cons _true_grey_Y' _camera_grey') { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map the original image through the lineariser to // get linear 0-1 RGB image _image' = hist_map linearising_lut.value image.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ cast_float @ recomb M) _image'; // measure again and compute dE76 _camera'' = im_measure _result.value 0 0 image.width image.height 6 4; _dEs = map abs_vec (_camera'' - _true_Lab).value; final_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; // map the original image through the lineariser to get // linear 0-1 RGB image image' = hist_map calib.linearising_lut image; // convert linear RGB camera to Lab result = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M image')); } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Interpolate_bilinear xfactor yfactor scale_im; _offset_im = resize Interpolate_bilinear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.26/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.26/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/7.26/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The innermost list objects become Group()'d. */ to_group x = Group x, is_list x && !contains_Group x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = (ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.26/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } ================================================ FILE: share/nip2/compat/7.26/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use line/arrow/rect x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_rect x; im = get_image x; select_rect x = Image mask { im = Image (get_image x); imc = make_xy im.width im.height; mask = imc?0 >= x.nleft & imc?0 < x.nright & imc?1 >= x.ntop & imc?1 < x.nbottom; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Interpolate_bilinear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Interpolate_bilinear f1 1 b1 {b1 = resize Interpolate_bilinear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.26/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/7.26/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st a) x { a:x = l; } } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.26/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = error ("get_format: unable to get format from " ++ print x); has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = error ("get_bits: unable to get bits from " ++ print x); has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = error ("get_bands: unable to get bands from " ++ print x); has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = error ("get_coding: unable to get coding from " ++ print x); has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = error ("get_xres: unable to get xres from " ++ print x); has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = error ("get_yres: unable to get yres from " ++ print x); has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = error ("get_xoffset: unable to get xoffset from " ++ print x); has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = error ("get_yoffset: unable to get yoffset from " ++ print x); has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = error ("get_image: unable to get image from " ++ print x); has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = error ("get_number: unable to get number from " ++ print x); has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = error ("get_real: unable to get real from " ++ print x); has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = error ("get_width: unable to get width from " ++ print x); has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = error ("get_height: unable to get height from " ++ print x); has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = error ("get_left: unable to get left from " ++ print x); has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = error ("get_top: unable to get top from " ++ print x); // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.26/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend") { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (_ "bad arguments to " ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx x = oo_unary_function greyc_op x, is_class x = greyc_im x, is_image x = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") { greyc_op = Operator "greyc" (greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im x = im_greyc x iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx mask x = oo_binary_function greyc_mask_op mask x, is_class mask = oo_binary'_function greyc_mask_op mask x, is_class x = greyc_im mask x, is_image mask && is_image x = error (_ "bad arguments" ++ " (" ++ print mask ++ ", " ++ print x ++ ") " ++ "to " ++ "greyc") { greyc_mask_op = Operator "greyc_mask" (greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im mask x = im_greyc_mask x mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize interp xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; // is this interpolation nearest-neighbour? is_nn x = x.type == Interpolate_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor, nearest neighbour // upscale by integer part, then affine to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by any factor, nearest neighbour // downscale by integer part, then affine to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor with affine = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 // downscale by any factor, bilinear // block shrink by integer factor, then resample to // exact with affine = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate_type.descriptions?interp.type ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // resize scale xfac yfac im = im_affinei_all im interp.value xfac 0 0 yfac 0 0; } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } draw_rect x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; } ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* Generate an ImageMagick (or GraphicsMagick) command suitable for * im_system_image. Use convert.exe in $VIPSHOME/bin, if it exists, otherwise * assume it's on the path somewhere. */ magick_command switch = join_sep " " [convert, "\"%s\"", switch, "\"%s\""] { prefs = Workspaces.Preferences; use_gm = prefs.USE_GRAPHICSMAGICK; name = if use_gm then "gm" else "convert"; exe = concat [name, expand "$EXEEXT"]; vipsexe = path_absolute [expand "$VIPSHOME", "bin", exe]; final_exe = vipsexe, search vipsexe != [] = exe; convert = join_sep " " [final_exe, "convert"], use_gm = final_exe; } /* Run a command on an image. See magick_command, for example. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } ================================================ FILE: share/nip2/compat/7.26/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; LUMINANCE = 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; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $LUMINANCE => LUMINANCE, $XRAY => XRAY, $IR => IR, $YUV => YUV, $RED_ONLY => RED_ONLY, $GREEN_ONLY => GREEN_ONLY, $BLUE_ONLY => BLUE_ONLY, $POWER_SPECTRUM => POWER_SPECTRUM, $HISTOGRAM => HISTOGRAM, $LUT => LUT, $XYZ => XYZ, $LAB => LAB, $CMC => CMC, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16 ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/7.28/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Colour Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; _result = map_unary chart x { chart in = measure 0 0 in.width in.height (to_real pacross) (to_real pdown) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.28/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_greyc_item = class Menupullright "_GREYCstoration" "noise-removing filter" { Denoise_item = class Menuaction "Denoise" "Noise-removing filter" { action x = class _result { _vislevel = 3; iterations = Scale "Iterations" 1 5 1; amplitude = Scale "Amplitude" 1 100 40; sharpness = Scale "Sharpness" 0 3 0.9; anisotropy = Scale "Anisotropy" 0 1 0.15; alpha = Scale "Noise scale" 0 5 0.6; sigma = Scale "Geometry regularity" 0 2 1.1; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) x; } } Enlarge_item = class Menuaction "Enlarge" "Enlarge image" { action x = class _result { _vislevel = 3; scale = Scale "Enlarge" 1 10 3; iterations = Scale "Iterations" 1 5 3; amplitude = Scale "Amplitude" 1 100 20; sharpness = Scale "Sharpness" 0 3 0.2; anisotropy = Scale "Anisotropy" 0 1 0.9; alpha = Scale "Noise scale" 0 5 0.1; sigma = Scale "Geometry regularity" 0 2 1.5; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) (resize Interpolate_bilinear (to_real scale) (to_real scale) x); } } } Filter_magick_item = class Menupullright "Magic_k" "various Image/Graphics Magick filters" { system command x = map_unary (system_image command) x; radius_widget = Scale "Radius" 1 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; print_colour triple = concat ["\"#", concat (map fmt triple), "\""] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; Background triple = class Colour "sRGB" triple { _flag = "-background " ++ print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Geometry_widget = class { _vislevel = 3; x = Expression "Y" 0; y = Expression "X" 0; hoffset = Expression "Horizontal offset" 10; voffset = Expression "Vertical offset" 10; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } Adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; radius = radius_widget; sigma = sigma_widget; command = magick_command (concat ["-adaptive-blur ", print radius.value, "x", print sigma.value]); _result = system command x; } } Adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; radius = radius_widget; sigma = sigma_widget; command = magick_command (concat ["-adaptive-sharpen ", print radius.value, "x", print sigma.value]); _result = system command x; } } Alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = alpha_widget; command = magick_command alpha._flag; _result = system command x; } } Annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = text_widget; font = Font_widget; geometry = Geometry_widget; gravity = gravity_widget; foreground = foreground_widget; antialias = antialias_widget; command = magick_command (join_sep " " [ font._flag, antialias._flag, gravity._flag, foreground._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\""]); _result = system command x; } } Swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = angle_widget; command = magick_command ("-swirl " ++ print angle.value); _result = system command x; } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/7.28/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "Histogram" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Interpolate_bilinear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/7.28/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp h v image, aspect = resize interp fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize interp fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 100; sy = Expression "Start y" 100; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/7.28/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.28 start_DATA = \ Math.def \ Image.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.28/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_And" "bitwise and of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_Or" "bitwise or of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "E_xclusive Or" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_Not" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Band_or_item = class Menuaction "Band O_r" "or the bands of an image together" { action im = map_unary (foldr1 bitwise_or @ bandsplit) im; } Band_and_item = class Menuaction "Band A_nd" "and the bands of an image together" { action im = map_unary (foldr1 bitwise_and @ bandsplit) im; } } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.28/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = class _result { _vislevel = 3; s = Expression "Size" 5; _result = Matrix (identity_matrix s.expr); } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = class _result { _vislevel = 3; s = Expression "Start value" 0; t = Expression "Step by" 1; e = Expression "End value" 5; _result = Matrix (transpose [series]) { series = [to_real s, to_real t.. to_real e]; } } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = class _result { _vislevel = 3; s = Expression "Size" 5; _result = Matrix_con (s.expr ** 2) 0 square { line = take s.expr [1, 1 ..]; square = replicate s.expr line; } } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = class _result { _vislevel = 3; r = Expression "Radius" 5; _result = Matrix_con (sum circle) 0 circle { line = [-r.expr .. r.expr]; xes = replicate (2 * r.expr + 1) line; yes = transpose xes; circle = map2 (map2 pyth) xes yes { pyth a b = 1, (a**2 + b**2) ** 0.5 <= r.expr = 0; } } } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.28/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list (or group) of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.28/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linear input", "Fit intercept from chart greyscale", "Linearize input from chart greyscale" ] 2; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure 0 0 image.width image.height 6 4 image.value; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix [[0, 0], [1, 1]], mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix (map2 cons _true_grey_Y' _camera_grey') { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map the original image through the lineariser to // get linear 0-1 RGB image _image' = hist_map linearising_lut.value image.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ cast_float @ recomb M) _image'; // measure again and compute dE76 _camera'' = im_measure _result.value 0 0 image.width image.height 6 4; _dEs = map abs_vec (_camera'' - _true_Lab).value; final_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; // map the original image through the lineariser to get // linear 0-1 RGB image image' = hist_map calib.linearising_lut image; // convert linear RGB camera to Lab result = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M image')); } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Interpolate_bilinear xfactor yfactor scale_im; _offset_im = resize Interpolate_bilinear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.28/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.28/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/7.28/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The innermost list objects become Group()'d. */ to_group x = Group x, is_list x && !contains_Group x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = (ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.28/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } ================================================ FILE: share/nip2/compat/7.28/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use line/arrow/rect x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_rect x; im = get_image x; select_rect x = Image mask { im = Image (get_image x); imc = make_xy im.width im.height; mask = imc?0 >= x.nleft & imc?0 < x.nright & imc?1 >= x.ntop & imc?1 < x.nbottom; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Interpolate_bilinear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Interpolate_bilinear f1 1 b1 {b1 = resize Interpolate_bilinear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.28/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/7.28/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st a) x { a:x = l; } } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.28/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = error ("get_format: unable to get format from " ++ print x); has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = error ("get_bits: unable to get bits from " ++ print x); has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = error ("get_bands: unable to get bands from " ++ print x); has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = error ("get_coding: unable to get coding from " ++ print x); has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = error ("get_xres: unable to get xres from " ++ print x); has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = error ("get_yres: unable to get yres from " ++ print x); has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = error ("get_xoffset: unable to get xoffset from " ++ print x); has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = error ("get_yoffset: unable to get yoffset from " ++ print x); has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = error ("get_image: unable to get image from " ++ print x); has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = error ("get_number: unable to get number from " ++ print x); has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = error ("get_real: unable to get real from " ++ print x); has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = error ("get_width: unable to get width from " ++ print x); has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = error ("get_height: unable to get height from " ++ print x); has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = error ("get_left: unable to get left from " ++ print x); has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = error ("get_top: unable to get top from " ++ print x); // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.28/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (_ "bad arguments to " ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx x = oo_unary_function greyc_op x, is_class x = greyc_im x, is_image x = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") { greyc_op = Operator "greyc" (greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im x = im_greyc x iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx mask x = oo_binary_function greyc_mask_op mask x, is_class mask = oo_binary'_function greyc_mask_op mask x, is_class x = greyc_im mask x, is_image mask && is_image x = error (_ "bad arguments" ++ " (" ++ print mask ++ ", " ++ print x ++ ") " ++ "to " ++ "greyc") { greyc_mask_op = Operator "greyc_mask" (greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im mask x = im_greyc_mask x mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize interp xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; // is this interpolation nearest-neighbour? is_nn x = x.type == Interpolate_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor, nearest neighbour // upscale by integer part, then affine to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by any factor, nearest neighbour // downscale by integer part, then affine to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor with affine = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 // downscale by any factor, bilinear // block shrink by integer factor, then resample to // exact with affine = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate_type.descriptions?interp.type ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // resize scale xfac yfac im = im_affinei_all im interp.value xfac 0 0 yfac 0 0; } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; } ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* Generate an ImageMagick (or GraphicsMagick) command suitable for * im_system_image. Use convert.exe in $VIPSHOME/bin, if it exists, otherwise * assume it's on the path somewhere. */ magick_command switch = join_sep " " [convert, "\"%s\"", switch, "\"%s\""] { prefs = Workspaces.Preferences; use_gm = prefs.USE_GRAPHICSMAGICK; name = if use_gm then "gm" else "convert"; exe = concat [name, expand "$EXEEXT"]; vipsexe = path_absolute [expand "$VIPSHOME", "bin", exe]; final_exe = vipsexe, search vipsexe != [] = exe; convert = join_sep " " [final_exe, "convert"], use_gm = final_exe; } /* Run a command on an image. See magick_command, for example. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } ================================================ FILE: share/nip2/compat/7.28/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/7.38/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.38/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_greyc_item = class Menupullright "_GREYCstoration" "noise-removing filter" { Denoise_item = class Menuaction "Denoise" "Noise-removing filter" { action x = class _result { _vislevel = 3; iterations = Scale "Iterations" 1 5 1; amplitude = Scale "Amplitude" 1 100 40; sharpness = Scale "Sharpness" 0 3 0.9; anisotropy = Scale "Anisotropy" 0 1 0.15; alpha = Scale "Noise scale" 0 5 0.6; sigma = Scale "Geometry regularity" 0 2 1.1; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) x; } } Enlarge_item = class Menuaction "Enlarge" "Enlarge image" { action x = class _result { _vislevel = 3; scale = Scale "Enlarge" 1 10 3; iterations = Scale "Iterations" 1 5 3; amplitude = Scale "Amplitude" 1 100 20; sharpness = Scale "Sharpness" 0 3 0.2; anisotropy = Scale "Anisotropy" 0 1 0.9; alpha = Scale "Noise scale" 0 5 0.1; sigma = Scale "Geometry regularity" 0 2 1.5; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) (resize Interpolate_bilinear (to_real scale) (to_real scale) x); } } } Filter_magick_item = class Menupullright "Magic_k" "various Image/Graphics Magick filters" { system command x = map_unary (system_image command) x; radius_widget = Scale "Radius" 1 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; print_colour triple = concat ["\"#", concat (map fmt triple), "\""] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; Background triple = class Colour "sRGB" triple { _flag = "-background " ++ print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Geometry_widget = class { _vislevel = 3; x = Expression "Y" 0; y = Expression "X" 0; hoffset = Expression "Horizontal offset" 10; voffset = Expression "Vertical offset" 10; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } Adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; radius = radius_widget; sigma = sigma_widget; command = magick_command (concat ["-adaptive-blur ", print radius.value, "x", print sigma.value]); _result = system command x; } } Adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; radius = radius_widget; sigma = sigma_widget; command = magick_command (concat ["-adaptive-sharpen ", print radius.value, "x", print sigma.value]); _result = system command x; } } Alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = alpha_widget; command = magick_command alpha._flag; _result = system command x; } } Annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = text_widget; font = Font_widget; geometry = Geometry_widget; gravity = gravity_widget; foreground = foreground_widget; antialias = antialias_widget; command = magick_command (join_sep " " [ font._flag, antialias._flag, gravity._flag, foreground._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\""]); _result = system command x; } } Swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = angle_widget; command = magick_command ("-swirl " ++ print angle.value); _result = system command x; } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/7.38/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Interpolate_bilinear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/7.38/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp h v image, aspect = resize interp fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize interp fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 100; sy = Expression "Start y" 100; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/7.38/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.38 start_DATA = \ Math.def \ Image.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.38/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.38/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.38/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.38/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Interpolate_bilinear xfactor yfactor scale_im; _offset_im = resize Interpolate_bilinear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.38/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.38/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/7.38/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart > 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.38/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/compat/7.38/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Interpolate_bilinear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Interpolate_bilinear f1 1 b1 {b1 = resize Interpolate_bilinear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.38/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/7.38/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.38/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.38/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_cache x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; patches = map (\p extract_area p?0 p?1 sample_width sample_height image) cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx x = oo_unary_function greyc_op x, is_class x = greyc_im x, is_image x = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") { greyc_op = Operator "greyc" (greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im x = im_greyc x iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx mask x = oo_binary_function greyc_mask_op mask x, is_class mask = oo_binary'_function greyc_mask_op mask x, is_class x = greyc_im mask x, is_image mask && is_image x = error (_ "bad arguments" ++ " (" ++ print mask ++ ", " ++ print x ++ ") " ++ "to " ++ "greyc") { greyc_mask_op = Operator "greyc_mask" (greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im mask x = im_greyc_mask x mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize interp xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; // is this interpolation nearest-neighbour? is_nn x = x.type == Interpolate_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor, nearest neighbour // upscale by integer part, then affine to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by any factor, nearest neighbour // downscale by integer part, then affine to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor with affine = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 // downscale by any factor, bilinear // block shrink by integer factor, then resample to // exact with affine = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate_type.descriptions?interp.type ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // resize scale xfac yfac im = im_affinei_all im interp.value xfac 0 0 yfac 0 0; } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; } ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* Generate an ImageMagick (or GraphicsMagick) command suitable for * im_system_image. Use convert.exe in $VIPSHOME/bin, if it exists, otherwise * assume it's on the path somewhere. */ magick_command switch = join_sep " " [convert, "\"%s\"", switch, "\"%s\""] { prefs = Workspaces.Preferences; use_gm = prefs.USE_GRAPHICSMAGICK; name = if use_gm then "gm" else "convert"; exe = concat [name, expand "$EXEEXT"]; vipsexe = path_absolute [expand "$VIPSHOME", "bin", exe]; final_exe = vipsexe, search vipsexe != [] = exe; convert = join_sep " " [final_exe, "convert"], use_gm = final_exe; } /* Run a command on an image. See magick_command, for example. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } ================================================ FILE: share/nip2/compat/7.38/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/7.40/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = monitor_profile, has_bands image && get_bands image == 3 = print_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/7.40/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 1.5; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 50; fs = Scale "Sharpen flat areas by" (-2) 5 1; js = Scale "Sharpen jaggy areas by" (-2) 5 2; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_hough_item = class Menupullright "_Hough Transform" "transform to parameter space" { Line_item = class Menuaction "_Line" "find straight line Hough transform" { action a = class _result { _vislevel = 3; pspace_width = Expression "Parameter space width" 64; pspace_height = Expression "Parameter space height" 64; _result = map_unary line a { line a = hough_line (to_real pspace_width) (to_real pspace_height) a; } } } Circle_item = class Menuaction "_Circle" "find circle Hough transform" { action a = class _result { _vislevel = 3; scale = Expression "Scale down parameter space by" 10; min_radius = Expression "Minimum radius" 10; max_radius = Expression "Maximum radius" 30; _result = map_unary circle a { circle a = hough_circle (to_real scale) (to_real min_radius) (to_real max_radius) a; } } } } Filter_greyc_item = class Menupullright "_GREYCstoration" "noise-removing filter" { Denoise_item = class Menuaction "Denoise" "Noise-removing filter" { action x = class _result { _vislevel = 3; iterations = Scale "Iterations" 1 5 1; amplitude = Scale "Amplitude" 1 100 40; sharpness = Scale "Sharpness" 0 3 0.9; anisotropy = Scale "Anisotropy" 0 1 0.15; alpha = Scale "Noise scale" 0 5 0.6; sigma = Scale "Geometry regularity" 0 2 1.1; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) x; } } Enlarge_item = class Menuaction "Enlarge" "Enlarge image" { action x = class _result { _vislevel = 3; scale = Scale "Enlarge" 1 10 3; iterations = Scale "Iterations" 1 5 3; amplitude = Scale "Amplitude" 1 100 20; sharpness = Scale "Sharpness" 0 3 0.2; anisotropy = Scale "Anisotropy" 0 1 0.9; alpha = Scale "Noise scale" 0 5 0.1; sigma = Scale "Geometry regularity" 0 2 1.5; dl = Scale "Spatial integration step" 0 1 0.8; da = Scale "Angular integration step" 0 90 30; gauss_prec = Scale "Precision" 1 10 2; interpolation = Option "Interpolation" ["Nearest-neighbour", "Bilinear", "Runge-Kutta"] 0; fast_approx = Toggle "Fast approximation" true; _result = greyc (to_real iterations) (to_real amplitude) (to_real sharpness) (to_real anisotropy) (to_real alpha) (to_real sigma) (to_real dl) (to_real da) (to_real gauss_prec) (to_real interpolation) (to_real fast_approx) (resize Interpolate_bilinear (to_real scale) (to_real scale) x); } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/7.40/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Interpolate_bilinear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; caption = Expression "Chart caption" "none"; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; xcaption = Expression "X axis caption" "none"; ycaption = Expression "Y axis caption" "none"; series_captions = Expression "Series captions" ["Band 0"]; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range ++ captions; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; captions = concat (map test caption_options) ++ [$series_captions => series_captions.expr] { caption_options = [ $caption => caption.expr, $xcaption => xcaption.expr, $ycaption => ycaption.expr ]; test x = [], value == "none" = [option_name => value] { [option_name, value] = x; } } image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/7.40/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp h v image, aspect = resize interp fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize interp fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 100; sy = Expression "Start y" 100; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/7.40/Magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate Magick.def 13-Apr-2014 snibgo Put "new image" items into sub-menu. New class VirtualPixlBack. 17-Apr-2014 snibgo Many small changes. A few new menu options. Created sub-menu for multi-input operations. 3-May-2014 jcupitt Put quotes around ( in shadow to help unix Last update: 17-Apr-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ // We don't need Noop. /*=== Magick_noop_item = class Menuaction "_Noop" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_testPar_item = class Menuaction "_TestPar" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "( +clone ) +append ", "\"%s\"" ]; _result = Magick.system command x; } } /* Removed Read_item and Write_item, much better to use nip2 load/save image. * Plus they can load all libMagick formats anyway. */ // Put "new image" items into sub-menu Magick_NewImageMenu_item = class Menupullright "_New image" "make a new image" { Magick_newcanvas_item = class Menuaction "_Solid colour" "make image of solid colour" { action = class _result { _vislevel = 3; size = Magick.Size_widget; colour = Magick.generalcol_widget; command = Magick.command [ size._flag, "\"canvas:" ++ colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_builtin_item = class Menuaction "_Built-in image" "create a built-in image" { action = class _result { _vislevel = 3; builtin = Magick.builtin_widget; command = Magick.command [ builtin._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_gradient_item = class Menuaction "_Gradient" "make a linear gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; topColour = Magick.generalcol_widget; bottomColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"gradient:", topColour._flag, "-", bottomColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } Magick_hald_item = class Menuaction "_Hald-clut image" "create an identity hald-clut image" { action = class _result { _vislevel = 3; order = Expression "order" 8; command = Magick.command [ "hald:" ++ print order.expr, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_pattern_item = class Menuaction "_Pattern" "create pattern" { action = class _result { _vislevel = 3; size = Magick.Size_widget; pattern = Magick.pattern_widget; command = Magick.command [ size._flag, pattern._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_plasma_item = class Menuaction "_Plasma image" "create plasma image" { action = class _result { _vislevel = 3; size = Magick.Size_widget; // FIXME? ColourA-ColourB. // FIXME? Allow plasma:fractal? command = Magick.command [ size._flag, "plasma:", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_radialgradient_item = class Menuaction "_Radial gradient" "make a radial gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; innerColour = Magick.generalcol_widget; outerColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"radial-gradient:", innerColour._flag, "-", outerColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } } // end Magick_NewImageMenu_item Magick_MultiMenu_item = class Menupullright "_Multiple inputs" "make an image from multiple images" { Magick_composite_item = class Menuaction "_Composite" "composite two images (without mask)" { action x y = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_compositeMask_item = class Menuaction "_Composite masked" "composite two images (with mask)" { action x y z = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system3 command x y z; } } // FIXME: other operations like remap that take another image as arguments are: // mask (pointless?), texture, tile (pointless?) // FIXME: operations that take a filename that isn't an image: // cdl, profile Magick_clut_item = class Menuaction "_Clut" "replace values using second image as colour look-up table" { action x y = class _result { _vislevel = 3; // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" command = Magick.command [ "\"%s\"", "\"%s\"", "-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_haldclut_item = class Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"", "-hald-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } // Encipher and decipher: key files can be text or image files. Magick_encipher_item = class Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { action x = class _result { _vislevel = 3; pathname = Pathname "Read key file" ""; isDecipher = Toggle "Decipher" false; command = Magick.command [ "\"%s\"", if pathname.value == "" then "" else ( ( if isDecipher then "-decipher " else "-encipher ") ++ ( "\"" ++ pathname.value ++ "\"" ) ), "\"%s\"" ]; _result = Magick.system command x; } } Magick_profile_item = class Menuaction "_Profile" "assigns/applies an ICC profile" { action x = class _result { _vislevel = 3; pathname1 = Pathname "Read profile file" ""; pathname2 = Pathname "Read profile file" ""; command = Magick.command [ "\"%s\"", if pathname1.value == "" then "" else ( "-profile " ++ "\"" ++ pathname1.value ++ "\""), if pathname2.value == "" then "" else ( "-profile " ++ "\"" ++ pathname2.value ++ "\""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_remap_item = class Menuaction "_Remap" "reduce colours to those in another image" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-remap", "\"%s\"", "\"%s\"" ]; _result = Magick.system2 command x y; } } } // end Magick_MultiMenu_item Magick_image_type_item = class Menuaction "_Image Type" "change image type" { action x = class _result { _vislevel = 3; imagetype = Magick.imagetype_widget; command = Magick.command [ "\"%s\"", imagetype._flag, "\"%s\"" ]; _result = Magick.system command x; } } sep2 = Menuseparator; Magick_alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = Magick.alpha_widget; command = Magick.command [ "\"%s\"", alpha._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = Magick.text_widget; font = Magick.Font_widget; geometry = Magick.AnnotGeometry_widget; gravity = Magick.gravity_widget; foreground = Magick.foreground_widget; undercol = Magick.undercol_widget; antialias = Magick.antialias_widget; command = Magick.command [ "\"%s\"", font._flag, antialias._flag, gravity._flag, foreground._flag, undercol._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoGamma_item = class Menuaction "_AutoGamma" "automatic gamma" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-gamma", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoLevel_item = class Menuaction "_AutoLevel" "automatic level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-level", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blurSharpMenu_item = class Menupullright "_Blur/Sharpen" "blur and sharpen" { Magick_adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; // note: adaptive-blur doesn't regard VP. command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-adaptive-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_blur_item = class Menuaction "_Blur" "blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gaussianBlur_item = class Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-gaussian-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_motionBlur_item = class Menuaction "_Motion Blur" "simulate motion blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; channels = Magick.ch_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-motion-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotationalBlur_item = class Menuaction "_RotationalBlur" "blur around the centre" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; angle = Scale "angle (degrees)" (-360) 360 20; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-radial-blur", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_selectiveBlur_item = class Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; threshold = Scale "Threshold (percent)" 0 100 50; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", intensity._flag, virtpixback._flag, channels._flag, "-selective-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print threshold.value ++ "%%", "\"%s\"" ]; _result = Magick.system command x; } } sep1 = Menuseparator; Magick_adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-adaptive-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sharpen_item = class Menuaction "_Sharpen" "sharpen" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_unsharpen_item = class Menuaction "_Unsharp" "sharpen with unsharp mask" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; gain = Scale "Gain" (-10) 10 1; threshold = Scale "Threshold" 0 1 0.05; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-unsharp", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print gain.value ++ "+" ++ print threshold.value, "\"%s\"" ]; _result = Magick.system command x; } } } // end BlurSharpMenu_item Magick_border_item = class Menuaction "_Border" "add border of given colour" { action x = class _result { _vislevel = 3; compose = Magick.compose_widget; width = Expression "Width" 3; bordercol = Magick.bordercol_widget; command = Magick.command [ "\"%s\"", compose._flag, bordercol._flag, "-border", print width.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_brightCont_item = class Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { action x = class _result { _vislevel = 3; bri = Scale "brightness" (-100) 100 0; con = Scale "contrast" (-100) 100 0; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-brightness-contrast", print bri.value ++ "x" ++ print con.value, "\"%s\"" ]; _result = Magick.system command x; } } // Note: canny requires ImageMagick 6.8.9-0 or later. Magick_canny_item = class Menuaction "_Canny" "detect a wide range of edges" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; lowPc = Scale "lower percent" 0 100 10; highPc = Scale "lower percent" 0 100 10; command = Magick.command [ "\"%s\"", "-canny", concat ["\"", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print lowPc.value ++ "%%+" ++ print highPc.value ++ "%%" ++ "\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_charcoal_item = class Menuaction "_Charcoal" "simulate a charcoal drawing" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; factor = Scale "factor" 0 50 1; command = Magick.command [ "\"%s\"", intensity._flag, "-charcoal", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_chop_item = class Menuaction "_Chop" "remove pixels from the interior" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-chop", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorize_item = class Menuaction "_Colorize" "colorize by given amount" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; val = Scale "value" 0 100 100; command = Magick.command [ "\"%s\"", foreground._flag, "-colorize", print val.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colors_item = class Menuaction "_Colors" "reduce number of colors" { action x = class _result { _vislevel = 3; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; quantize = Magick.colorspace_widget; colors = Expression "Colours" 3; command = Magick.command [ "\"%s\"", "-quantize", quantize._flag, "-treedepth", print treedepth.expr, dither._flag, "-colors", print colors.expr, "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: color-matrix? Magick_colorspace_item = class Menuaction "_Colourspace" "convert to arbitrary colourspace" { action x = class _result { _vislevel = 3; colsp = Magick.colorspace_widget; command = Magick.command [ "\"%s\"", "-colorspace", colsp._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorspaceGray_item = class Menuaction "_Colourspace gray" "convert to gray using given intensity method" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-colorspace gray", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrast_item = class Menuaction "_Contrast" "increase or reduce the contrast" { action x = class _result { _vislevel = 3; isReduce = Toggle "reduce contrast" false; command = Magick.command [ "\"%s\"", (if isReduce then "+" else "-") ++ "contrast", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrastStretch_item = class Menuaction "_Contrast stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-contrast-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: convolve (bias, kernel) Magick_crop_item = class Menuaction "_Crop" "cut out a rectangular region" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-crop", geometry._flag, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_deskew_item = class Menuaction "_Deskew" "straighten the image" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; // FIXME: toggle auto-crop? command = Magick.command [ "\"%s\"", "-deskew", "\"" ++ print threshold.value ++ "%%\"", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_despeckle_item = class Menuaction "_Despeckle" "reduce the speckles" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-despeckle", "\"%s\"" ]; _result = Magick.system command x; } } Magick_distort_item = class Menuaction "_Distort" "distort using a method and arguments" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; distort = Magick.distort_widget; args = String "Arguments" "1,0"; isPlus = Toggle "Extend to show entire image" false; command = Magick.command [ "\"%s\"", virtpixback._flag, (if isPlus then "+" else "-") ++ "distort", distort._flag, args.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_draw_item = class Menuaction "_Draw" "annotate with one or more graphic primitives" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; command = Magick.command [ "\"%s\"", foreground._flag, "-draw", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_edge_item = class Menuaction "_Edge" "detect edges" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-edge", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_emboss_item = class Menuaction "_Emboss" "emboss" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-emboss", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_enhance_item = class Menuaction "_Enhance" "enhance a noisy image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-enhance", "\"%s\"" ]; _result = Magick.system command x; } } Magick_equalize_item = class Menuaction "_Equalize" "equalize the histogram" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-equalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_evaluate_item = class Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; operation = Magick.evaluate_widget; val = Expression "value" 5; isPc = Toggle "Value is percent" false; command = Magick.command [ "\"%s\"", channels._flag, operation._flag, print val.expr ++ if isPc then "%%" else "", "\"%s\"" ]; _result = Magick.system command x; } } Magick_extent_item = class Menuaction "_Extent" "set the image size and offset" { action x = class _result { _vislevel = 3; background = Magick.background_widget; compose = Magick.compose_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, background._flag, gravity._flag, "-extent", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_FlipFlopMenu_item = class Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { Magick_flip_item = class Menuaction "_Flip vertically" "mirror upside-down" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flip", "\"%s\"" ]; _result = Magick.system command x; } } Magick_flop_item = class Menuaction "_Flop horizontally" "mirror left-right" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flop", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transpose_item = class Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transpose +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transverse_item = class Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transverse +repage", "\"%s\"" ]; _result = Magick.system command x; } } } // end Magick_FlipFlopMenu_item Magick_floodfill_item = class Menuaction "_Floodfill" "recolour neighbours that match" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; fuzz = Magick.fuzz_widget; coordinate = Magick.coordinate_widget; // -draw "color x,y floodfill" command = Magick.command [ "\"%s\"", foreground._flag, "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-draw \" color", coordinate._flag, "floodfill \"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_frame_item = class Menuaction "_Frame" "surround with border or beveled frame" { action x = class _result { _vislevel = 3; border = Magick.bordercol_widget; compose = Magick.compose_widget; matte = Magick.mattecol_widget; geometry = Magick.FrameGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, border._flag, matte._flag, "-frame", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_function_item = class Menuaction "_Function" "evaluate a function on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; function = Magick.function_widget; // FIXME: explain values; use sensible defaults. values = String "values" "0,0,0,0"; command = Magick.command [ "\"%s\"", channels._flag, "-function", function._flag, values.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_fx_item = class Menuaction "_Fx" "apply a mathematical expression" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; interpolate = Magick.interpolate_widget; virtpixback = Magick.VirtualPixelBack_widget; args = String "Expression" "u*1/2"; command = Magick.command [ "\"%s\"", channels._flag, interpolate._flag, virtpixback._flag, "-fx", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_gamma_item = class Menuaction "_Gamma" "apply a gamma correction" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; gamma = Magick.gamma_widget; command = Magick.command [ "\"%s\"", channels._flag, "-gamma", print gamma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradient_item = class Menuaction "_Gradient" "apply a linear gradient" { action x = class _result { _vislevel = 3; colourA = Magick.generalcol_widget; colourB = Magick.generalcol_widget; position = Option "colourA is at" [ "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"] 0; _baryArg = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 = "dunno"; command = Magick.command [ "\"%s\"", "-sparse-color barycentric \"" ++ _baryArg ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradientCorn_item = class Menuaction "_Gradient corners" "apply a bilinear gradient between the corners" { action x = class _result { _vislevel = 3; colour_top_left = Magick.generalcol_widget; colour_top_right = Magick.generalcol_widget; colour_bottom_left = Magick.generalcol_widget; colour_bottom_right = Magick.generalcol_widget; command = Magick.command [ "\"%s\"", "-sparse-color bilinear \"" ++ "0,0," ++ colour_top_left._flag ++ ",%%[fx:w-1],0" ++ colour_top_right._flag ++ ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", "+depth", "\"%s\"" ]; _result = Magick.system command x; } } Magick_histogram_item = class Menuaction "_Histogram" "make a histogram image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-define histogram:unique-colors=false histogram:" ++ "\"%s\"" ]; _result = Magick.system command x; } } Magick_implode_item = class Menuaction "_Implode" "implode pixels about the center" { action x = class _result { _vislevel = 3; factor = Scale "factor" 0 20 1; interpolate = Magick.interpolate_widget; // FIXME: virtual-pixel? command = Magick.command [ "\"%s\"", interpolate._flag, "-implode", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_level_item = class Menuaction "_Level" "adjust the level of channels" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "black point" (-100) 200 0; wht = Scale "white point" (-100) 200 100; gam = Scale "gamma" 0 30 1; isPc = Toggle "Levels are percent" true; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level", "\"" ++ print blk.value ++ "," ++ print wht.value ++ (if isPc then "%%" else "") ++ "," ++ print gam.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_levelCols_item = class Menuaction "_Level colors" "adjust levels to given colours" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; colour_black = Magick.generalcol_widget; colour_white = Magick.generalcol_widget; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level-colors", "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_linearStretch_item = class Menuaction "_Linear stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", channels._flag, "-linear-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_magnify_item = class Menuaction "_Magnify" "double the size of the image with pixel art scaling" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-magnify", "\"%s\"" ]; _result = Magick.system command x; } } Magick_modulate_item = class Menuaction "_Modulate" "modulate brightness, saturation and hue" { action x = class _result { _vislevel = 3; modcolsp = Magick.ModColSp_widget; bright = Scale "brightness" 0 200 100; sat = Scale "saturation" 0 200 100; hue = Scale "hue" 0 200 100; command = Magick.command [ "\"%s\"", modcolsp._flag, "-modulate", print bright.value ++ "," ++ print sat.value ++ "," ++ print hue.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_monochrome_item = class Menuaction "_Monochrome" "transform to black and white" { action x = class _result { _vislevel = 3; // FIXME: also intensity? intensity = Magick.intensity_widget; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; command = Magick.command [ "\"%s\"", intensity._flag, dither._flag, "-treedepth", print treedepth.expr, "-monochrome", "\"%s\"" ]; _result = Magick.system command x; } } Magick_morphology_item = class // See http://www.imagemagick.org/Usage/morphology/ Menuaction "_Morphology" "apply a morphological method" { action x = class _result { _vislevel = 3; method = Magick.morphmeth_widget; iter = Expression "Iterations (-1=repeat until done)" 1; kernel = Magick.kernel_widget; // FIXME: custom kernel eg "3x1+2+0:1,0,0" // width x height + offsx + offsy : {w*h values} // each value is 0.0 to 1.0 or "NaN" or "-" // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 // but // ring takes: radius1, radius2, scale // rectangle and comet take: width x height + offsx + offsy // blur takes: radius x sigma // FIXME: for now, simply allow any string input. kernel_arg = String "Kernel arguments" ""; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-morphology", method._flag ++ ":" ++ print iter.expr, kernel._flag ++ (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_negate_item = class Menuaction "_Negate" "negate" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-negate", "\"%s\"" ]; _result = Magick.system command x; } } Magick_addNoise_item = class Menuaction "_add Noise" "add noise" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; attenuate = Scale "attenuate" 0 1.0 1.0; noise = Magick.noise_widget; command = Magick.command [ "\"%s\"", channels._flag, "-attenuate", print attenuate.value, noise._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_normalize_item = class Menuaction "_Normalize" "normalize" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-normalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_opaque_item = class Menuaction "_Opaque" "change this colour to the fill colour" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; fill = Magick.foreground_widget; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", fill._flag, channels._flag, "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "opaque", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_paint_item = class Menuaction "_Paint" "simulate an oil painting" { action x = class _result { _vislevel = 3; rad = Expression "radius" 10; command = Magick.command [ "\"%s\"", "-paint", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } /*=== FIXME Bug; remove for now. Polaroid_item = class Menuaction "_Polaroid" "simulate a polaroid picture" { action x = class _result { _vislevel = 3; angle = Scale "angle" (-90) 90 20; command = Magick.command [ "\"%s\"", "-polaroid", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_posterize_item = class Menuaction "_Posterize" "reduce to (n) levels per channel" { action x = class _result { _vislevel = 3; levels = Expression "levels" 3; command = Magick.command [ "\"%s\"", "-posterize", print levels.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_raise_item = class Menuaction "_Raise" "lighten or darken image edges" { action x = class _result { _vislevel = 3; thk = Expression "Thickness" 3; command = Magick.command [ "\"%s\"", "-raise", print thk.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_resize_item = class Menuaction "_Resize" "resize to given width and height" { action x = class _result { _vislevel = 3; filter = Magick.filter_widget; type = Magick.ResizeType_widget; width = Scale "Width" 1 100 10; height = Scale "Height" 1 100 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", filter._flag, "-" ++ type._flag, "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "!") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_roll_item = class Menuaction "_Roll" "roll an image horizontally or vertically" { action x = class _result { _vislevel = 3; rollx = Expression "X" 3; rolly = Expression "Y" 3; command = Magick.command [ "\"%s\"", "-roll", (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotate_item = class Menuaction "_Rotate" "rotate" { action x = class _result { _vislevel = 3; angle = Scale "angle (degrees)" (-360) 360 20; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, "+distort", "SRT", print angle.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -segment, but cluster-threshold should be percentage of image area. Magick_sepia_item = class Menuaction "_Sepia tone" "simulate a sepia-toned photo" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; command = Magick.command [ "\"%s\"", "-sepia-tone", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shade_item = class Menuaction "_Shade" "shade with a distant light source" { action x = class _result { _vislevel = 3; azimuth = Scale "Azimuth (degrees)" (-360) 360 0; elevation = Scale "Elevation (degrees)" 0 90 45; command = Magick.command [ "\"%s\"", "-shade", print azimuth.value ++ "x" ++ print elevation.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_shadow_item = class Menuaction "_Shadow" "simulate a shadow" { action x = class _result { _vislevel = 3; shadowCol = Magick.generalcol_widget; opacity = Scale "Opacity (percent)" 0 100 75; sigma = Scale "Sigma" 0 30 2; // FIXME: make offsets a single widget? offsx = Scale "X-offset" (-20) 20 4; offsy = Scale "Y-offset" (-20) 20 4; arePc = Toggle "offsets are percentages" false; // FIXME: raw operation creates page offset, which vips dislikes. // So we take this futher, compositing with source. command = Magick.command [ "\"%s\"", "\"(\" +clone", "-background", "\"" ++ shadowCol._flag ++ "\"", "-shadow", concat [ "\"", print opacity.value, "x", print sigma.value, (if offsx.value >= 0 then "+" else ""), print offsx.value, (if offsy.value >= 0 then "+" else ""), print offsy.value, (if arePc then "%%" else ""), "\"" ], "\")\" +swap -background None -layers merge", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shave_item = class Menuaction "_Shave" "shave pixels from the edges" { action x = class _result { _vislevel = 3; width = Scale "Width" 0 50 10; height = Scale "Height" 0 50 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", "-shave", "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shear_item = class Menuaction "_Shear" "shear along the x-axis and/or y-axis" { action x = class _result { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-shear", print shearX.expr ++ "x" ++ print shearY.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sigmoid_item = class Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; contrast = Scale "contrast" 0 30 3; midpoint = Scale "mid-point (percent)" 0 100 50; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "sigmoidal-contrast", "\"" ++ print contrast.value ++ "x" ++ print midpoint.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_sketch_item = class Menuaction "_Sketch" "simulate a pencil sketch" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; command = Magick.command [ "\"%s\"", "-sketch", print radius.value ++ "x" ++ print sigma.value ++ (if angle >= 0 then ("+" ++ print angle.value) else ""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_solarize_item = class Menuaction "_Solarize" "negate all pixels above a threshold level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-solarize", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -sparse-color needs abitrary list of {x,y,colour}. Magick_splice_item = class Menuaction "_Splice" "splice a colour into the image" { action x = class _result { _vislevel = 3; background = Magick.background_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", background._flag, gravity._flag, "-splice", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_spread_item = class Menuaction "_Spread" "displace pixels by random amount" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; amount = Expression "Amount (pixels)" 5; command = Magick.command [ "\"%s\"", virtpixback._flag, "-spread", print amount.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_statistic_item = class Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { action x = class _result { _vislevel = 3; width = Expression "Width" 5; height = Expression "Height" 5; statisticType = Magick.StatType_widget; command = Magick.command [ "\"%s\"", "-statistic", statisticType._flag, print width.expr ++ "x" ++ print height.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = Magick.angle_widget; command = Magick.command [ "\"%s\"", "-swirl", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_thresholdMenu_item = class Menupullright "_Threshold" "make black or white" { Magick_threshold_item = class Menuaction "_Threshold" "apply black/white threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blackThreshold_item = class Menuaction "_Black threshold" "where below threshold set to black" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-black-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_whiteThreshold_item = class Menuaction "_White threshold" "where above threshold set to white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-white-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_latThreshold_item = class Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { action x = class _result { _vislevel = 3; width = Expression "Width" 10; height = Expression "Height" 10; offset = Scale "Offset (percent)" (-100) 100 0; // note: "-lat" doesn't respond to channels command = Magick.command [ "\"%s\"", "-lat", concat ["\"", print width.expr, "x", print height.expr, (if offset.value >= 0 then "+" else ""), print offset.value, "%%\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_randThreshold_item = class Menuaction "_Random Threshold" "between specified limits, apply random threshold" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; low = Scale "Low threshold" 0 100 10; high = Scale "High threshold" 0 100 90; isPc = Toggle "Thresholds are percent" true; command = Magick.command [ "\"%s\"", channels._flag, "-random-threshold", "\"" ++ print low.value ++ "x" ++ print high.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } } // end ThresholdMenu_item // Note: alternatives include: // convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif Magick_tile_item = class Menuaction "_Tile" "fill given size with tiled image" { action x = class _result { _vislevel = 3; size = Magick.Size_widget; command = Magick.command [ size._flag, "tile:" ++ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_tint_item = class Menuaction "_Tint" "apply a tint" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; amount = Scale "amount (percent)" 0 100 50; command = Magick.command [ "\"%s\"", foreground._flag, "-tint", // snibgo note: although the amount is a percentage, it doesn't need "%" character. print amount.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_transparent_item = class Menuaction "_Transparent" "make this colour transparent" { action x = class _result { _vislevel = 3; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "transparent", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_trim_item = class Menuaction "_Trim" "trims away border" { action x = class _result { _vislevel = 3; fuzz = Magick.fuzz_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-trim +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_uniqueCols_item = class Menuaction "_Unique colours" "discard all but one of any pixel color" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-unique-colors", "\"%s\"" ]; _result = Magick.system command x; } } Magick_vignette_item = class Menuaction "_Vignette" "soften the edges in vignette style" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; rx = Scale "Rolloff x (percent)" 0 100 10; ry = Scale "Rolloff y (percent)" 0 100 10; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-vignette", print radius.value ++ "x" ++ print sigma.value ++ (if rx.value >= 0 then "+" else "") ++ print rx.value ++ (if ry.value >= 0 then "+" else "") ++ print ry.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_wave_item = class Menuaction "_Wave" "shear the columns into a sine wave" { action x = class _result { _vislevel = 3; amplitude = Scale "Amplitude (pixels)" 0 100 10; wavelength = Scale "Wavelength (pixels)" 0 100 10; command = Magick.command [ "\"%s\"", "-wave", print amplitude.value ++ "x" ++ print wavelength.value, "\"%s\"" ]; _result = Magick.system command x; } } ================================================ FILE: share/nip2/compat/7.40/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.40 start_DATA = \ Math.def \ Image.def \ Magick.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ Preferences.ws \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _magick.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.40/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/7.40/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/7.40/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/7.40/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/7.40/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Interpolate_bilinear xfactor yfactor scale_im; _offset_im = resize Interpolate_bilinear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/7.40/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/7.40/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/7.40/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart > 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/7.40/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/compat/7.40/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Interpolate_bilinear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Interpolate_bilinear f1 1 b1 {b1 = resize Interpolate_bilinear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/7.40/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/7.40/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.40/_magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate _magick.def Add 0-ary and 2-ary system Put utility funcs into a Magick class 11-Apr-2014 snibgo Added VirtualPixelBack for cases where background is only relevant when VP=Background 17-Apr-2014 snibgo Many small changes. 2-May-2014 jcupitt Added Magick.version 30-June-2014 Put single-quotes around command exe to help win 1-July-2014 Automatically fall back to gm if we can't find convert 17-July-2014 better GM support Last update: 17-July-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ /* Put these in a class to avoid filling the main namespace with IM stuff. */ Magick = class { // turn $PATH into [["comp1", "comp2"], ["comp1", "comp2"] ..] system_search_path = map path_parse (split (equal path_sep) (expand "$PATH")) { path_sep = ':', expand "$SEP" == "/" = ';'; } // the first name[.exe] on $PATH, or "" search_for name = hits?0, hits != [] = "" { exe_name = name ++ expand "$EXEEXT"; form_path p = path_absolute (p ++ [exe_name]); paths = map form_path system_search_path; hits = dropwhile (equal []) (map search paths); } // first gm on path, or "" gm_path = search_for "gm"; // first convert on $PATH, or "" // we check for the convert we ship first convert_path = vips_convert, vips_convert != "" = search_for "convert" { // the convert we ship with the vips binary on some platforms, or "" vips_convert = search (path_absolute convert) { vipshome = path_parse (expand "$VIPSHOME"); convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; } } use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; // Are we in GM or IM mode? use_gm = true, use_gm_pref && gm_path != "" = false, !use_gm_pref && convert_path != "" = false, convert_path != "" = true, gm_path != "" = error "neither IM nor GM executable found"; command_path = gm_path, use_gm = convert_path; // try to get the version as eg. [6, 7, 7, 10] // GM versions are smaller, typically [1, 3, 18] version = map parse_int (split (member ".-") version_string) { [output] = vips_call "system" ["'" ++ command_path ++ "' -version"] [$log=>true]; version_string = (split (equal ' ') output)?1, use_gm = (split (equal ' ') output)?2; } // make a command-line ... args is a [str] we join with spaces command args = "'" ++ command_path ++ "' " ++ join_sep " " args' { args' = ["convert"] ++ args, use_gm = args; } // capabilities ... different versions support different features, we // turn features on and off based on these // would probably be better to test for caps somehow has_intensity = false, use_gm = version?0 > 6 || version?1 > 7; has_channel = false, use_gm = version?0 > 6 || version?1 > 7; system0 cmd = system_image0 cmd; system cmd x = map_unary (system_image cmd) x; system2 cmd x y = map_binary (system_image2 cmd) x y; system3 cmd x y z = map_trinary (system_image3 cmd) x y z; radius_widget = Scale "Radius" 0 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle (degrees)" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; gamma_widget = Scale "Gamma" 0 10 1; colors_widget = Scale "Colors" 1 10 3; resize_widget = Scale "Resize (percent)" 0 500 100; fuzz_widget = Scale "Fuzz (percent)" 0 100 0; blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; // a colour with no enclosing quotes ... use this if we know there are // some quotes at an outer level print_colour_nq triple = concat ["#", concat (map fmt triple)] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } // we need the quotes because # is the comment character in *nix print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; GeneralCol triple = class Colour "sRGB" triple { _flag = print_colour_nq triple; Colour_edit space triple = this.GeneralCol triple; } generalcol_widget = GeneralCol [0, 0, 0]; Background triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" false; _flag = "-background " ++ if isNone then "None" else print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Bordercol triple = class Colour "sRGB" triple { _flag = "-bordercolor " ++ print_colour triple; Colour_edit space triple = this.Bordercol triple; } bordercol_widget = Bordercol [0, 0, 0]; Mattecol triple = class Colour "sRGB" triple { _flag = "-mattecolor " ++ print_colour triple; Colour_edit space triple = this.Mattecol triple; } mattecol_widget = Mattecol [189, 189, 189]; // FIXME: Undercolour, like many others, can have alpha channel. // How does user input this? With a slider? Undercol triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" true; _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); Colour_edit space triple = this.Undercol triple; } undercol_widget = Undercol [0, 0, 0]; changeCol_widget = class { _vislevel = 3; colour = GeneralCol [0, 0, 0]; fuzz = fuzz_widget; nonMatch = Toggle "change non-matching colours" false; } Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Remove", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Builtin builtin = class Option_string "Builtin" [ // See http://www.imagemagick.org/script/formats.php "rose:", "logo:", "wizard:", "granite:", "netscape:" ] builtin { _flag = builtin; Option_edit caption labels value = this.Builtin labels?value; } builtin_widget = Builtin "rose:"; channels_widget = class { // FIXME? Can we grey-out alpha when we have no alpha channel, // show CMY(K) instead of RGB(K) etc? // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. ChanR valueR = class Toggle "Red" valueR { _flag = "R", valueR = ""; Toggle_edit caption valueR = this.ChanR valueR; } channelR = ChanR true; ChanG valueG = class Toggle "Green" valueG { _flag = "G", valueG = ""; Toggle_edit caption valueG = this.ChanG valueG; } channelG = ChanG true; ChanB valueB = class Toggle "Blue" valueB { _flag = "B", valueB = ""; Toggle_edit caption valueB = this.ChanB valueB; } channelB = ChanB true; ChanK valueK = class Toggle "Black" valueK { _flag = "K", valueK = ""; Toggle_edit caption valueK = this.ChanK valueK; } channelK = ChanK true; ChanA valueA = class Toggle "Alpha" valueA { _flag = "A", valueA = ""; Toggle_edit caption valueA = this.ChanA valueA; } channelA = ChanA false; ChanSy valueSy = class Toggle "Sync" valueSy { _flag = ",sync", valueSy = ""; Toggle_edit caption valueSy = this.ChanSy valueSy; } channelSy = ChanSy true; _rgbka = concat [channelR._flag, channelG._flag, channelB._flag, channelK._flag, channelA._flag ]; _flag = "", _rgbka == "" || !has_channel = concat [ "-channel ", _rgbka, channelSy._flag ]; } ch_widget = channels_widget; Colorspace colsp = class Option_string "Colorspace" [ "CIELab", "CMY", "CMYK", "Gray", "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "Lab", "LCH", "LCHab", "LCHuv", "LMS", "Log", "Luv", "OHTA", "Rec601Luma", "Rec601YCbCr", "Rec709Luma", "Rec709YCbCr", "RGB", "scRGB", "sRGB", "Transparent", "XYZ", "YCbCr", "YDbDr", "YCC", "YIQ", "YPbPr", "YUV" ] colsp { _flag = colsp; Option_edit caption labels value = this.Colorspace labels?value; } colorspace_widget = Colorspace "sRGB"; Compose comp = class Option_string "Compose method" [ "Atop", "Blend", "Blur", "Bumpmap", "ChangeMask", "Clear", "ColorBurn", "ColorDodge", "Colorize", "CopyBlack", "CopyBlue", "CopyCyan", "CopyGreen", "Copy", "CopyMagenta", "CopyOpacity", "CopyRed", "CopyYellow", "Darken", "DarkenIntensity", "DivideDst", "DivideSrc", "Dst", "Difference", "Displace", "Dissolve", "Distort", "DstAtop", "DstIn", "DstOut", "DstOver", "Exclusion", "HardLight", "Hue", "In", "Lighten", "LightenIntensity", "LinearBurn", "LinearDodge", "LinearLight", "Luminize", "Mathematics", "MinusDst", "MinusSrc", "Modulate", "ModulusAdd", "ModulusSubtract", "Multiply", "None", "Out", "Overlay", "Over", "PegtopLight", "PinLight", "Plus", "Replace", "Saturate", "Screen", "SoftLight", "Src", "SrcAtop", "SrcIn", "SrcOut", "SrcOver", "VividLight", "Xor" ] comp { _flag = "-compose " ++ comp; Option_edit caption labels value = this.Compose labels?value; } compose_widget = Compose "Over"; // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack coordinate_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; _flag = concat [print x.expr, ",", print y.expr]; }; Distort distort = class Option_string "Distort" [ "Affine", "AffineProjection", "ScaleRotateTranslate", "SRT", "Perspective", "PerspectiveProjection", "BilinearForward", "BilinearReverse", "Polynomial", "Arc", "Polar", "DePolar", "Barrel", "BarrelInverse", "Shepards", "Resize" ] distort { _flag = distort; Option_edit caption labels value = this.Distort labels?value; } distort_widget = Distort "SRT"; Dither dither = class Option_string "Dither" [ "None", "FloydSteinberg", "Riemersma" ] dither { _flag = "-dither " ++ dither; Option_edit caption labels value = this.Dither labels?value; } dither_widget = Dither "FloydSteinberg"; Evaluate eval = class Option_string "Evaluate operation" [ "Abs", "Add", "AddModulus", "And", "Cos", "Cosine", "Divide", "Exp", "Exponential", "GaussianNoise", "ImpulseNoise", "LaplacianNoise", "LeftShift", "Log", "Max", "Mean", "Median", "Min", "MultiplicativeNoise", "Multiply", "Or", "PoissonNoise", "Pow", "RightShift", "Set", "Sin", "Sine", "Subtract", "Sum", "Threshold", "ThresholdBlack", "ThresholdWhite", "UniformNoise", "Xor" ] eval { _flag = "-evaluate " ++ eval; Option_edit caption labels value = this.Evaluate labels?value; } evaluate_widget = Evaluate "Add"; Filter filt = class Option_string "Filter" [ "default", "Bartlett", "Blackman", "Bohman", "Box", "Catrom", "Cosine", "Cubic", "Gaussian", "Hamming", "Hann", "Hermite", "Jinc", "Kaiser", "Lagrange", "Lanczos", "Lanczos2", "Lanczos2Sharp", "LanczosRadius", "LanczosSharp", "Mitchell", "Parzen", "Point", "Quadratic", "Robidoux", "RobidouxSharp", "Sinc", "SincFast", "Spline", "Triangle", "Welch" ] filt { _flag = if filt == "default" then "" else "-filter " ++ filt; Option_edit caption labels value = this.Filter labels?value; } filter_widget = Filter "default"; Function func = class Option_string "Function" [ "Polynomial", "Sinusoid", "Arcsin", "Arctan" ] func { _flag = func; Option_edit caption labels value = this.Function labels?value; } function_widget = Function "Polynomial"; // "Polynomial (a[n], a[n-1], ... a[1], a[0])", // "Sinusoid (freq, phase, amp, bias)", // "Arcsin (width, centre, range, bias)", // "Arctan (slope, centre, range, bias)" Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; ImageType imagetype = class Option_string "Image type" [ "Bilevel", "ColorSeparation", "ColorSeparationAlpha", "ColorSeparationMatte", "Grayscale", "GrayscaleAlpha", "GrayscaleMatte", "Optimize", "Palette", "PaletteBilevelAlpha", "PaletteBilevelMatte", "PaletteAlpha", "PaletteMatte", "TrueColorAlpha", "TrueColorMatte", "TrueColor" ] imagetype { _flag = "-type " ++ imagetype; Option_edit caption labels value = this.ImageType labels?value; } imagetype_widget = ImageType "TrueColor"; Intensity intensity = class Option_string "Intensity (gray conversion)" [ "Average", "Brightness", "Lightness", "MS", "Rec601Luma", "Rec601Luminance", "Rec709Luma", "Rec709Luminance", "RMS" ] intensity { _flag = "-intensity " ++ intensity, has_intensity = ""; Option_edit caption labels value = this.Intensity labels?value; } intensity_widget = Intensity "Rec709Luminance"; Interpolate interp = class Option_string "Interpolate" [ "default", "Average", "Average4", "Average9", "Average16", "Background", "Bilinear", "Blend", "Integer", "Mesh", "Nearest", "NearestNeighbor", "Spline" ] interp { _flag = if interp == "default" then "" else "-interpolate " ++ interp; Option_edit caption labels value = this.Interpolate labels?value; } interpolate_widget = Interpolate "default"; Kernel kernel = class Option_string "Kernel" [ "Unity", "Gaussian", "DoG", "LoG", "Blur", "Comet", "Binomial", "Laplacian", "Sobel", "FreiChen", "Roberts", "Prewitt", "Compass", "Kirsch", "Diamond", "Square", "Rectangle", "Disk", "Octagon", "Plus", "Cross", "Ring", "Peaks", "Edges", "Corners", "Diagonals", "LineEnds", "LineJunctions", "Ridges", "ConvexHull", "ThinSe", "Skeleton", "Chebyshev", "Manhattan", "Octagonal", "Euclidean" // FIXME: custom kernel ] kernel { _flag = kernel; Option_edit caption labels value = this.Kernel labels?value; } kernel_widget = Kernel "Unity"; ModColSp msp = class Option_string "modulate colorspace" [ "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "LCH" ] msp { _flag = "-set option:modulate:colorspace " ++ msp; Option_edit caption labels value = this.ModColSp labels?value; } ModColSp_widget = ModColSp "HSL"; MorphMeth morph = class Option_string "Method" [ "Correlate", "Convolve", "Dilate", "Erode", "Close", "Open", "DilateIntensity", "ErodeIntensity", "CloseIntensity", "OpenIntensity", "Smooth", "EdgeOut", "EdgeIn", "Edge", "TopHat", "BottomHat", "HitAndMiss", "Thinning", "Thicken", "Distance", "IterativeDistance" ] morph { _flag = morph; Option_edit caption labels value = this.MorphMeth labels?value; } morphmeth_widget = MorphMeth "Dilate"; Noise noise = class Option_string "Noise" [ "Gaussian", "Impulse", "Laplacian", "Multiplicative", "Poisson", "Random", "Uniform" ] noise { _flag = "+noise " ++ noise; Option_edit caption labels value = this.Noise labels?value; } noise_widget = Noise "Gaussian"; Pattern pattern = class Option_string "Noise" [ // See http://www.imagemagick.org/script/formats.php "bricks", "checkerboard", "circles", "crosshatch", "crosshatch30", "crosshatch45", "gray0", "gray5", "gray10", "gray15", "gray20", "gray25", "gray30", "gray35", "gray40", "gray45", "gray50", "gray55", "gray60", "gray65", "gray70", "gray75", "gray80", "gray85", "gray90", "gray95", "gray100", "hexagons", "horizontal", "horizontal2", "horizontal3", "horizontalsaw", "hs_bdiagonal", "hs_cross", "hs_diagcross", "hs_fdiagonal", "hs_horizontal", "hs_vertical", "left30", "left45", "leftshingle", "octagons", "right30", "right45", "rightshingle", "smallfishscales", "vertical", "vertical2", "vertical3", "verticalbricks", "verticalleftshingle", "verticalrightshingle", "verticalsaw" ] pattern { _flag = "pattern:" ++ pattern; Option_edit caption labels value = this.Pattern labels?value; } pattern_widget = Pattern "bricks"; ResizeType resizet = class Option_string "Resize type" [ "resize", "scale", "sample", "adaptive-resize" ] resizet { _flag = resizet; Option_edit caption labels value = this.ResizeType labels?value; } ResizeType_widget = ResizeType "resize"; Size_widget = class { _vislevel = 3; width = Expression "Width (pixels)" 64; height = Expression "Height (pixels)" 64; _flag = "-size " ++ print width.expr ++ "x" ++ print height.expr; }; StatType statt = class Option_string "Statistic type" [ "Gradient", "Maximum", "Mean", "Median", "Minimum", "Mode", "Nonpeak", "StandardDeviation" ] statt { _flag = statt; Option_edit caption labels value = this.StatType labels?value; } StatType_widget = StatType "Mean"; VirtualPixel vp = class Option_string "Virtual pixel" [ "Background", "Black", "CheckerTile", "Dither", "Edge", "Gray", "HorizontalTile", "HorizontalTileEdge", "Mirror", "None", "Random", "Tile", "Transparent", "VerticalTile", "VerticalTileEdge", "White" ] vp { _flag = "-virtual-pixel " ++ vp; _isBackground = (vp == "Background"); Option_edit caption labels value = this.VirtualPixel labels?value; } VirtualPixel_widget = VirtualPixel "Edge"; VirtualPixelBack_widget = class { virtpix = Magick.VirtualPixel_widget; background = Magick.background_widget; _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") ++ virtpix._flag; } Geometry_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; AnnotGeometry_widget = class { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print shearX.expr, "x", print shearY.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; OffsetGeometry_widget = class { _vislevel = 3; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; WhxyGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; FrameGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; outbev = Expression "Outer bevel thickness" 0; inbev = Expression "Inner bevel thickness" 0; _flag = concat [print x.expr, "x", print y.expr, format outbev, format inbev] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } } ================================================ FILE: share/nip2/compat/7.40/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/7.40/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_tile_cache_random x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } decode im = oo_unary_function decode_op im, is_class im = decode_im im, is_image im = error (_ "bad arguments to " ++ "decode") { decode_op = Operator "decode" decode Operator_type.COMPOUND_REWRAP false; decode_im im = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK = im_rad2float im, get_coding im == Image_coding.RAD = im; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; image' = decode image; patches = map (\p extract_area p?0 p?1 sample_width sample_height image') cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx x = oo_unary_function greyc_op x, is_class x = greyc_im x, is_image x = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") { greyc_op = Operator "greyc" (greyc iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im x = im_greyc x iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx mask x = oo_binary_function greyc_mask_op mask x, is_class mask = oo_binary'_function greyc_mask_op mask x, is_class x = greyc_im mask x, is_image mask && is_image x = error (_ "bad arguments" ++ " (" ++ print mask ++ ", " ++ print x ++ ") " ++ "to " ++ "greyc") { greyc_mask_op = Operator "greyc_mask" (greyc_mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx) Operator_type.COMPOUND_REWRAP false; greyc_im mask x = im_greyc_mask x mask iterations amplitude sharpness anisotropy alpha sigma dl da gauss_prec interpolation fast_approx; } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize interp xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; // is this interpolation nearest-neighbour? is_nn x = x.type == Interpolate_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor, nearest neighbour // upscale by integer part, then affine to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by any factor, nearest neighbour // downscale by integer part, then affine to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor with affine = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 // downscale by any factor, bilinear // block shrink by integer factor, then resample to // exact with affine = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate_type.descriptions?interp.type ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // resize scale xfac yfac im = im_affinei_all im interp.value xfac 0 0 yfac 0 0; } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; } ss = len xes; sx = sum xes; sy = sum yes; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* One image in, one out. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } /* Two images in, one out. */ system_image2 command x1 x2 = oo_binary_function system_image2_op x1 x2, is_class x1 = oo_binary'_function system_image2_op x1 x2, is_class x2 = system x1 x2, is_image x1 && is_image x2 = error (_ "bad arguments to " ++ "system_image2") { system_image2_op = Operator "system_image2" (system_image2 command) Operator_type.COMPOUND_REWRAP false; system x1 x2 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Three images in, one out. */ system_image3 command x1 x2 x3 = oo_binary_function system_image2_op x2 x3, is_class x2 = oo_binary'_function system_image2_op x2 x3, is_class x3 = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 = error (_ "bad arguments to " ++ "system_image3") { system_image2_op = Operator "system_image2" (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; system x1 x2 x3 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2, x3], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Zero images in, one out. */ system_image0 command = Image image_out { [image_out] = vips_call "system" [command] [ $out => true, $out_format => "%s.tif" ]; } hough_line w h x = oo_unary_function hough_line_op x, is_class x = hline (to_real w) (to_real h) x, is_image x = error (_ "bad arguments to " ++ "hough_line") { hough_line_op = Operator "hough_line" (hough_line w h) Operator_type.COMPOUND_REWRAP false; hline w h x = pspace { [pspace] = vips_call "hough_line" [x] [ $width => w, $height => h ]; } } hough_circle s mn mx x = oo_unary_function hough_circle_op x, is_class x = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x = error (_ "bad arguments to " ++ "hough_circle") { hough_circle_op = Operator "hough_circle" (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; hcircle s mn mx x = pspace { [pspace] = vips_call "hough_circle" [x] [ $scale => s, $min_radius => mn, $max_radius => mx ]; } } ================================================ FILE: share/nip2/compat/7.40/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; to_image dpi = extract_bands 0 3 (graph_export_image (to_real dpi) this); } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/7.8/Capture.def ================================================ /* make a new capture image */ Capture_video = class Image value { // shortcut to prefs _prefs = Workspaces.Preferences; device = _prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] _prefs.VIDEO_CHANNEL; brightness = Slider 0 32767 _prefs.VIDEO_BRIGHTNESS; colour = Slider 0 32767 _prefs.VIDEO_COLOUR; contrast = Slider 0 32767 _prefs.VIDEO_CONTRAST; hue = Slider 0 32767 _prefs.VIDEO_HUE; frames_hint = "Average this many frames:"; frames = Slider 0 100 _prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" _prefs.VIDEO_MONO; crop = Toggle "Crop image" _prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value brightness.value colour.value contrast.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = _prefs.VIDEO_CROP_LEFT; top = _prefs.VIDEO_CROP_TOP; width = min_pair _prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair _prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_hint = "Stretch vertically by this factor:"; aspect_ratio = _prefs.VIDEO_ASPECT; value = frame' { frame = edit_crop.value, crop = _raw_grab.value; frame' = colour_transform Image_type.sRGB Image_type.B_W frame, mono = frame; } } #separator /* use white image w to correct image i */ Light_correct w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } /* equalize bands in region r */ White_balance r = map_unary wb r { wb region = clip2fmt region.format (region.image * Vector facs) { target = mean region; facs = map (divide target) (map mean (bandsplit region)); } } /* remove features larger than a certain size from image x */ Smooth_image x = map_unary smooth x { smooth image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; feature = Slider 1 50 20; value = im_resize_linear (im_shrink image.value feature.value feature.value) image.width image.height; } } #separator /* calculate RGB -> LAB transform from an image of a Macbeth colour chart */ Calibrate_chart image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; // get macbeth data file to use macbeth = Filename "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = im_measure image.value 0 0 image.width image.height 6 4; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'); // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it linearising_lut = Image _linear_lut; // map the original image through the lineariser to get linear 0-1 // RGB image _image' = im_maplut image.value linearising_lut.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab value = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb M _image')); // measure again and compute dE76 _camera'' = im_measure value 0 0 image.width image.height 6 4; _dEs = map abs_vec (map Vector (_camera'' - _true_Lab).value); final_dE76 = mean (Vector _dEs); _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = _macbeth_names ? _worst ++ " (patch " ++ print (_worst + 1) ++ ")"; } /* apply RGB -> LAB transform to an image */ Calibrate_image calib image = class Image value { _check_args = [ [calib, "calib", check_instance "Calibrate_chart"], [image, "image", check_Image] ] ++ super._check_args; // map the original image through the lineariser to get linear 0-1 // RGB image _image' = im_maplut image.value calib.linearising_lut.value; // convert linear RGB camera to Lab value = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M _image')); } ================================================ FILE: share/nip2/compat/7.8/Colour.def ================================================ /* Save a bit of typing. */ _colour_conv from to x = map_unary (colour_transform from to) x; /* convert Mono to various formats */ Mono_to = class { /* convert mono colourspace to mono colourspace */ Mono x = _colour_conv Image_type.B_W Image_type.B_W x; /* convert mono colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.B_W Image_type.XYZ x; /* convert mono colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.B_W Image_type.YXY x; /* convert mono colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.B_W Image_type.LAB x; /* convert mono colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.B_W Image_type.LCH x; /* convert mono colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.B_W Image_type.UCS x; /* convert mono colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.B_W Image_type.RGB x; /* convert mono colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.B_W Image_type.sRGB x; /* convert mono colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.B_W Image_type.LABQ x; /* convert mono colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.B_W Image_type.LABS x; } /* convert XYZ to various formats */ XYZ_to = class { /* convert XYZ colourspace to mono colourspace */ Mono x = _colour_conv Image_type.XYZ Image_type.B_W x; /* convert XYZ colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.XYZ Image_type.XYZ x; /* convert XYZ colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.XYZ Image_type.YXY x; /* convert XYZ colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.XYZ Image_type.LAB x; /* convert XYZ colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.XYZ Image_type.LCH x; /* convert XYZ colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.XYZ Image_type.UCS x; /* convert XYZ colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.XYZ Image_type.RGB x; /* convert XYZ colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.XYZ Image_type.sRGB x; /* convert XYZ colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.XYZ Image_type.LABQ x; /* convert XYZ colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.XYZ Image_type.LABS x; } /* convert Yxy to various formats */ Yxy_to = class { /* convert Yxy colourspace to mono colourspace */ Mono x = _colour_conv Image_type.YXY Image_type.B_W x; /* convert Yxy colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.YXY Image_type.XYZ x; /* convert Yxy colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.YXY Image_type.YXY x; /* convert Yxy colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.YXY Image_type.LAB x; /* convert Yxy colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.YXY Image_type.LCH x; /* convert Yxy colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.YXY Image_type.UCS x; /* convert Yxy colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.YXY Image_type.RGB x; /* convert Yxy colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.YXY Image_type.sRGB x; /* convert Yxy colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.YXY Image_type.LABQ x; /* convert Yxy colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.YXY Image_type.LABS x; } /* convert Lab to various formats */ Lab_to = class { /* convert Lab colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LAB Image_type.B_W x; /* convert Lab colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LAB Image_type.XYZ x; /* convert Lab colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LAB Image_type.YXY x; /* convert Lab colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LAB Image_type.LAB x; /* convert Lab colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LAB Image_type.LCH x; /* convert Lab colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LAB Image_type.UCS x; /* convert Lab colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LAB Image_type.RGB x; /* convert Lab colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LAB Image_type.sRGB x; /* convert Lab colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LAB Image_type.LABQ x; /* convert Lab colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LAB Image_type.LABS x; } /* convert LCh to various formats */ LCh_to = class { /* convert LCh colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LCH Image_type.B_W x; /* convert LCh colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LCH Image_type.XYZ x; /* convert LCh colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LCH Image_type.YXY x; /* convert LCh colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LCH Image_type.LAB x; /* convert LCh colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LCH Image_type.LCH x; /* convert LCh colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LCH Image_type.UCS x; /* convert LCh colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LCH Image_type.RGB x; /* convert LCh colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LCH Image_type.sRGB x; /* convert LCh colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LCH Image_type.LABQ x; /* convert LCh colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LCH Image_type.LABS x; } /* convert UCS to various formats */ UCS_to = class { /* convert UCS colourspace to mono colourspace */ Mono x = _colour_conv Image_type.UCS Image_type.B_W x; /* convert UCS colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.UCS Image_type.XYZ x; /* convert UCS colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.UCS Image_type.YXY x; /* convert UCS colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.UCS Image_type.LAB x; /* convert UCS colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.UCS Image_type.LCH x; /* convert UCS colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.UCS Image_type.UCS x; /* convert UCS colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.UCS Image_type.RGB x; /* convert UCS colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.UCS Image_type.sRGB x; /* convert UCS colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.UCS Image_type.LABQ x; /* convert UCS colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.UCS Image_type.LABS x; } /* convert RGB to various formats */ RGB_to = class { /* convert RGB colourspace to mono colourspace */ Mono x = _colour_conv Image_type.RGB Image_type.B_W x; /* convert RGB colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.RGB Image_type.XYZ x; /* convert RGB colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.RGB Image_type.YXY x; /* convert RGB colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.RGB Image_type.LAB x; /* convert RGB colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.RGB Image_type.LCH x; /* convert RGB colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.RGB Image_type.UCS x; /* convert RGB colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.RGB Image_type.RGB x; /* convert RGB colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.RGB Image_type.sRGB x; /* convert RGB colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.RGB Image_type.LABQ x; /* convert RGB colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.RGB Image_type.LABS x; } /* convert sRGB to various formats */ sRGB_to = class { /* convert sRGB colourspace to mono colourspace */ Mono x = _colour_conv Image_type.sRGB Image_type.B_W x; /* convert sRGB colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.sRGB Image_type.XYZ x; /* convert sRGB colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.sRGB Image_type.YXY x; /* convert sRGB colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.sRGB Image_type.LAB x; /* convert sRGB colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.sRGB Image_type.LCH x; /* convert sRGB colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.sRGB Image_type.UCS x; /* convert sRGB colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.sRGB Image_type.RGB x; /* convert sRGB colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.sRGB Image_type.sRGB x; /* convert sRGB colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.sRGB Image_type.LABQ x; /* convert sRGB colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.sRGB Image_type.LABS x; } /* convert LabQ to various formats */ LabQ_to = class { /* convert LabQ colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LABQ Image_type.B_W x; /* convert LabQ colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LABQ Image_type.XYZ x; /* convert LabQ colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LABQ Image_type.YXY x; /* convert LabQ colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LABQ Image_type.LAB x; /* convert LabQ colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LABQ Image_type.LCH x; /* convert LabQ colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LABQ Image_type.UCS x; /* convert LabQ colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LABQ Image_type.RGB x; /* convert LabQ colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LABQ Image_type.sRGB x; /* convert LabQ colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LABQ Image_type.LABQ x; /* convert LabQ colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LABQ Image_type.LABS x; } /* convert LabS to various formats */ LabS_to = class { /* convert LabS colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LABS Image_type.B_W x; /* convert LabS colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LABS Image_type.XYZ x; /* convert LabS colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LABS Image_type.YXY x; /* convert LabS colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LABS Image_type.LAB x; /* convert LabS colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LABS Image_type.LCH x; /* convert LabS colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LABS Image_type.UCS x; /* convert LabS colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LABS Image_type.RGB x; /* convert LabS colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LABS Image_type.sRGB x; /* convert LabS colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LABS Image_type.LABQ x; /* convert LabS colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LABS Image_type.LABS x; } #separator /* recombine image bands with an editable matrix */ Colour_recombination in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; matrix = Matrix_rec (identity_matrix image.bands); value = recomb matrix image.value; } } /* colour temperature conversions */ Colour_temperature = class { /* convert XYZ from D65 to D50 ... use the Bradford approximation */ D65XYZ_to_D50XYZ in = map_unary (colour_unary im_D652D50) in; /* convert XYZ from D50 to D65 ... use the Bradford approximation */ D50XYZ_to_D65XYZ in = map_unary (colour_unary im_D502D65) in; } /* various colour difference metrics */ dE_ = class { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ _apply_cvt cvt x = cvt x, is_instanceof "Image" x || is_instanceof "Colour" x || is_image x = x; _diff cvt in1 in2 = abs_vec (_apply_cvt cvt in1 - _apply_cvt cvt in2); /* Converter to LAB. */ _lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ _ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; /* calculate delta-E CIE76 for two objects */ CIE76 in1 in2 = map_binary (_diff _lab_cvt) in1 in2; /* calculate delta-E00 (CIEDE2000) for two objects */ CIE00 in1 in2 = map_binary (colour_binary "im_dE00_fromLab" im_dE00_fromLab) in1 in2; /* calculate delta-E CMC(1:1) for two objects */ UCS in1 in2 = map_binary (_diff _ucs_cvt) in1 in2; } #separator /* apply a coloured tint to a monochrome image */ Tint_mono_image in = map_unary apply_tint in { apply_tint in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; tint = Colour "Lab" [50, 0, 0]; value = image_set_type Image_type.LAB (fancytint inp l_tint a_tint b_tint) { // input image ... to L only inp_lab = colour_transform_to Image_type.LAB in.value; inp = inp_lab?0; // make sure tint is LAB (might be edited) lab_tint = colour_transform_to Image_type.LAB tint; // selected lab l_tint = lab_tint.value?0; a_tint = lab_tint.value?1; b_tint = lab_tint.value?2; // fancy tint function ... don't tint black and white fancytint im l a b = im ++ ima ++ imb { mod = (100 - im) / (100 - l), im > l = im / l; backgr = image_new in.width in.height 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; ima = mod * (backgr + a); imb = mod * (backgr + b); } } } } /* displace neutral axis in LAB colourspace */ Adjust_cast image = map_unary widget image { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; green_red = Slider (-20) 20 0; blue_yellow = Slider (-20) 20 0; value = (colour_transform_to (get_type image) image'').value { image' = colour_transform_to Image_type.LAB image; image'' = image' + Vector [0, green_red.value, blue_yellow.value]; } } } /* displace h, scale LC in LCh colourspace */ Adjust_hue_saturation_brightness image = map_unary widget image { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; hue = Slider 0 360 0; saturation = Slider 0.01 5 1; brightness = Slider 0.01 5 1; value = (colour_transform_to (get_type image) image'').value { image' = colour_transform_to Image_type.LCH image; image'' = image' * Vector [bv, sv, 1] + Vector [0, 0, hv]; bv = brightness.value; sv = saturation.value; hv = hue.value; } } } /* find pixels with a similar colour */ Similar_colour image = map_unary match image { match image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; target_patch = Region image (20 - image.xoffset) (20 - image.yoffset) 10 10; target_colour = Colour_from_image target_patch; dE_threshold = Slider 0 100 10; value = (dE_.CIE76 image target_colour < dE_threshold).value; } } /* plot an ab scatter histogram */ Plot_ab_scatter image = map_unary widget image { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; bins = 8; value = bg * (((90 / mx) * hist) ++ blk) { lab = colour_transform_to Image_type.LAB image.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins ab; mx = max hist; bg = lab_slice bins 1; blk = 1 + im_black bins bins 2; } } } ================================================ FILE: share/nip2/compat/7.8/Filter.def ================================================ /* Some useful masks. */ _filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; _filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; _filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; _filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; _filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; _filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; /* 3x3 blur of image */ Blur x = map_unary (conv _filter_blur) x; /* 3x3 sharpen of image */ Sharpen x = map_unary (conv _filter_sharp) x; /* 1-pixel displacement emboss */ Emboss x = map_unary (conv _filter_emboss) x; /* 3x3 median filter of image */ Median x = map_unary (rank 3 3 5) x; /* 3x3 laplacian of image */ Laplacian x = map_unary (conv _filter_laplacian) x; /* 3x3 Sobel edge detect of image */ Sobel x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv _filter_sobel im; b = conv (rot270 _filter_sobel) im; } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 _filter_lindet); images = map (converse conv im) masks; } } #separator /* custom convolution of an image */ Custom_convolution in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 off off image.width image.height, border = image' { conv_op = im_conv_raw, !separable && type == 0 = im_convsep_raw, separable && type == 0 = im_convf_raw, !separable && type == 1 = im_convsepf_raw, separable && type == 1 = error "boink!"; image' = conv_op image.value matrix; off = (matrix.width + 1) / 2; } } } /* blur with a radius and shape */ Custom_blur in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; radius = Slider 1 50 1; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; value = im_convsep in.value mask { /* Make a square mask. */ mask_sq_line = map (const 1) [1 .. 2 * radius.value - 1]; mask_sq_sum = foldr1 add mask_sq_line; mask_sq_sep = Matrix_con mask_sq_sum 0 [mask_sq_line]; /* Make a gaussian mask. sigma is not really related * to radius very well, sort-of bodge it. */ mask_g = im_gauss_imask (radius.value / 2) 0.2; mask_g_line = mask_g.value ? (mask_g.height / 2); mask_g_sum = foldr1 add mask_g_line; mask_g_sep = Matrix_con mask_g_sum 0 [mask_g_line]; mask = mask_sq_sep, shape.value == 0 = mask_g_sep; } } } /* cored sharpen of L only in LAB image */ Custom_sharpen in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; size = Option "Mask size" [ "3 x 3", "5 x 5", "7 x 7", "9 x 9", "11 x 11" ] 0; smooth_threshold = Slider 0 5 1.5; brighten_max = Slider 1 50 10; darken_max = Slider 1 50 50; flat_sharp = Slider (-2) 5 1; jaggy_sharp = Slider (-2) 5 2; value = im_sharpen in.value [3, 5, 7, 9, 11]?size smooth_threshold.value brighten_max.value darken_max.value flat_sharp.value jaggy_sharp.value; } } /* custom rank filter of an image */ Custom_rank in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; window_size_hint = "Length of side of window to pass " ++ "over image:"; window_size = 3; rank = (int) ((window_size * window_size + 1) / 2); border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 off off image.width image.height, border = image' { image' = im_rank_raw image.value window_size window_size rank; off = (window_size + 1) / 2; } } } /* statistical difference of an image */ Statistical_difference in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; window_size_hint = "Length of side of window to pass " ++ "over image:"; window_size = 11; target_mean = 128; target_mean_weight = Slider 0 1 0.8; target_deviation = 50; target_deviation_weight = Slider 0 1 0.8; border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 off off image.width image.height, border = image' { image' = im_stdif_raw image.value target_mean_weight.value target_mean target_deviation_weight.value target_deviation window_size window_size; off = (window_size + 1) / 2; } } } #separator /* various ideal fourier filters */ Ideal_filter = class { _preview_size = 64; _high_low image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value 0 0 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value 0 0 0 0; } _ring image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 6; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value ring_width.value 0 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value ring_width.value 0 0 0; } _band image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 12; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; } /* high or low pass ideal filter */ High_low x = map_unary _high_low x; /* ring pass or reject ideal filter */ Ring x = map_unary _ring x; /* band pass or reject ideal filter */ Band x = map_unary _band x; } /* Gaussian fourier filters */ Gaussian_filter = class { _preview_size = 64; _high_low image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 4; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value amplitude_cutoff.value 0 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value amplitude_cutoff.value 0 0 0; } _ring image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 10; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; } _band image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 16; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; } value = im_flt_image_freq image.value _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; } /* high or low pass Gaussian filter */ High_low x = map_unary _high_low x; /* ring pass or reject Gaussian filter */ Ring x = map_unary _ring x; /* band pass or reject Gaussian filter */ Band x = map_unary _band x; } /* various Butterworth fourier filters */ Butterworth_filter = class { _preview_size = 64; _high_low image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 2; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type order.value frequency_cutoff.value amplitude_cutoff.value 0 0; } value = im_flt_image_freq image.value _type order.value frequency_cutoff.value amplitude_cutoff.value 0 0; } _ring image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 8; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; } value = im_flt_image_freq image.value _type order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; } _band image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; order = Slider 1 10 2; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 14; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; } value = im_flt_image_freq image.value _type order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; } /* high or low pass Butterworth filter */ High_low x = map_unary _high_low x; /* ring pass or reject Butterworth filter */ Ring x = map_unary _ring x; /* band pass or reject Butterworth filter */ Band x = map_unary _band x; } ================================================ FILE: share/nip2/compat/7.8/Format.def ================================================ /* various operations to convert numeric precision */ Convert_format_to = class { /* convert to unsigned 8 bit [0, 255] */ unsigned_8bit x = map_unary cast_unsigned_char x; /* convert to signed 8 bit [-128, 127] */ signed_8bit x = map_unary cast_signed_char x; /* convert to unsigned 16 bit [0, 65535] */ unsigned_16bit x = map_unary cast_unsigned_short x; /* convert to signed 16 bit [-32768, 32767] */ signed_16bit x = map_unary cast_signed_short x; /* convert to unsigned 32 bit [0, 4294967295] */ unsigned_32bit x = map_unary cast_unsigned_int x; /* convert to signed 32 bit [-2147483648, 2147483647] */ signed_32bit x = map_unary cast_signed_int x; /* convert to IEEE 32 bit floating point */ float_32bit x = map_unary cast_float x; /* convert to IEEE 64 bit floating point */ float_64bit x = map_unary cast_double x; /* convert to 64 bit complex (2 x IEEE 32 bit floating point) */ complex_64bit x = map_unary cast_complex x; /* convert to 128 bit complex (2 x IEEE 64 bit floating point) */ complex_128bit x = map_unary cast_double_complex x; } /* linear scale of pixel values to fit range 0-255 */ Scale_to_byte x = map_unary scale x; #separator /* try to make a matrix out of an object */ Convert_to_matrix in = map_unary to_matrix in; /* try to make an image out of an object */ Convert_to_image in = map_unary to_image in; #separator /* measure average value of a set of patches */ Matrix_from_colour_chart x = map_unary chart x { chart image = class Matrix value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; pacross = 6; pdown = 4; value = (im_measure image.value 0 0 image.width image.height pacross pdown).value; /* Hmm, not very helpful, we throw edits away. */ Matrix_vips_edit value scale offset filename display = chart image; } } /* make a synthetic colour chart image from a matrix */ Colour_chart_from_matrix matrix = map_unary build_chart matrix { build_chart matrix = class Image value { _check_args = [ [matrix, "matrix", check_Matrix] ] ++ super._check_args; patches_across = 6; patches_down = 4; patch_width = 50; patch_height = 50; border_width = 0; value = imagearray_assemble overlap overlap patch_table { overlap = -border_width; // patch numbers for row starts rowstart = map (multiply patches_across) [0 .. patches_down - 1]; // assemble patches ... each one a pixel value patches = map (take patches_across) (map (converse drop matrix.value) rowstart); // make an n-band constant image from eg. [1,2,3] patch v = image_new patch_width patch_height (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } #separator /* make a colour from the average values in an image */ Colour_from_image image = map_unary test image { test x = build_chart x, is_instanceof "Image" x = build_chart (Image pixel), is_instanceof "Point" x = error ("Colour_from_image: arg not Image or Point: " ++ print x) { pixel = x.image.extract_area x.left x.top 1 1; } build_chart image = class Colour colour_space value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; colour_space = table.lookup 1 0 type, table.present 1 type = error ("Colour_from_image: unable to make Colour " ++ "from " ++ Image_type.type_names.lookup 1 0 type ++ " image") { table = Image_type.colour_spaces; type = im_header_int "Type" image.value; } value = map mean (bandsplit image.value); } } /* make a constant image from a colour patch */ Image_from_colour x = map_unary build x { build c = class Image value { _check_args = [ [c, "c", check_Colour] ] ++ super._check_args; width = 64; height = 64; value = image_new width height 3 Image_format.FLOAT Image_coding.NOCODING (get_type c) c 0 0; } } #separator /* try to break object in into a list of components */ Decompose in = map_unary dec in { dec x = bandsplit x, is_instanceof "Image" x = map Vector x.value, is_instanceof "Matrix_base" x = x.value, is_instanceof "Vector" x || is_instanceof "Real" x = error "Decompose: not Image/Matrix/Vector/Real"; } /* try to put a list of objects together into a large single object */ Compose x = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof (is_instanceof "Image") x = Vector (map get_value x), is_listof (is_instanceof "Real") x = Matrix (map get_value x), is_listof (is_instanceof "Vector") x = map_unary Compose x, is_list x = error "Compose: not list of Image/Vector/Real/image/real" { get_value x = x.value; } ================================================ FILE: share/nip2/compat/7.8/Histogram.def ================================================ /* find histogram of x */ Hist_find x = map_unary hist_find x; /* find n-dimensional histogram from n-band image */ Hist_find_nD in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; // default to something small-ish bins = 8; value = hist_find_nD bins image.value; } } /* map image x through histogram hist */ Hist_map hist x = map_binary hist_map hist x; /* find cumulative histogram of hist */ Hist_cumulative x = map_unary hist_cum x; /* find normalised histogram of hist */ Hist_normalise x = map_unary hist_norm x; /* find LUT which matches hist in to hist ref */ Hist_match in ref = map_binary hist_match in ref; #separator /* histogram equalisation */ Hist_equalise = class { /* histogram equalise x globally */ Global x = map_unary hist_equalize x; /* local histogram equalisation using region r as window */ Local r = map_unary (hist_equalize_local r.width r.height) r.image; } /* slice a line of pixels along a guide line and display as a histogram */ Guide_slice in = map_unary widget in { widget guide = class Image value { _check_args = [ [guide, "Guide", check_Guide] ] ++ super._check_args; value = image_set_type Image_type.HISTOGRAM slice { slice = guide.image.extract_area guide.image.rect.left guide.top guide.width 1, is_instanceof "HGuide" guide = guide.image.extract_area guide.left guide.image.rect.top 1 guide.height; } } } ================================================ FILE: share/nip2/compat/7.8/Image.def ================================================ /* take a copy of x */ Duplicate x = map_unary copy x; /* crop image x */ Crop x = map_unary build_widget x { build_widget image = widget image (image.width * 0.25 - image.xoffset) (image.height * 0.25 - image.yoffset) (image.width * 0.5) (image.height * 0.5); widget image left top width height = class Region image left top width height { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 4; Region_edit image left top width height = widget image left top width height; } } /* insert image b into image a */ Insert a b = class Image value { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _check_all = [ [a.format == b.format && a.coding == b.coding && a.bands == b.bands, "a.format == b.format && a.coding == b.coding && " ++ "a.bands == b.bands"], [a.width >= b.width && a.height >= b.height, "First image should be able to enclose second"] ]; _vislevel = 3; place = Area a ((a.width - b.width) / 2) ((a.height - b.height) / 2) b.width b.height; value = im_insert_noexpand a' b'' place.left place.top { a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; } } /* join two images left/right or up/down */ Join = class { _check_ab_args a b = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_ab_all a b = [ [a.format == b.format && a.coding == b.coding && a.bands == b.bands, "a.format == b.format && a.coding == b.coding && " ++ "a.bands == b.bands"] ]; /* join two images left-right */ Left_right a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; _check_all = _check_ab_all a b ++ super._check_all; shim = Slider 0 100 0; background_colour = 0; align = Option "Alignment" ["Top", "Centre", "Bottom"] 1; value = im2 { w = a.width + b.width + shim.value; h = max_pair a.height b.height; bg = image_new w h a.bands a.format a.coding a.type background_colour 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = im_insert_noexpand bg a.value 0 (ya?align); im2 = im_insert_noexpand im1 b.value (a.width + shim.value) (yb?align); } } /* join two images top-bottom */ Top_bottom a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; _check_all = _check_ab_all a b ++ super._check_all; shim = Slider 0 100 0; background_colour = 0; align = Option "Alignment" ["Left", "Centre", "Right"] 1; value = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim.value; bg = image_new w h a.bands a.format a.coding a.type background_colour 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = im_insert_noexpand bg a.value (xa?align) 0; im2 = im_insert_noexpand im1 b.value (xb?align) (a.height + shim.value); } } /* join a 2-D array of images */ Array x = class Image value { hspacing = Slider (-100) (100) 0; vspacing = Slider (-100) (100) 0; value = imagearray_assemble (-hspacing.value) (-vspacing.value) x' { x' = map (map getval) x; getval x = x.value, is_class x = x; } } } /* morph images to match (needs the rubbersheet plugin) */ Rubber = class { _rubber_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.bilinear; _rubber_order = Option "order" ["0", "1", "2", "3"] 1; _rubber_edges = Toggle "Wrap image edges" false; /* find a transform which will turn sample image into reference image */ Rubber_find reference sample = class Matrix transformation { _vislevel = 3; // controls order = _rubber_order; interp = _rubber_interp; edges = _rubber_edges; max_error = 0.3; max_iterations = 10; // transform _result = resample sample.value reference.value max_error max_iterations order.value interp.value edges.value; // results transformed_image = Image (_result?0); transformation = (_result?1).value; final_error = _result?2; } /* apply a transform to an image */ Rubber_apply transform image = class Image value { // controls interp = _rubber_interp; edges = _rubber_edges; value = im_transform image.value transform interp.value edges.value; } /* change a transformation's scale factor */ Rubber_scale transform = class Matrix scaled_transform { factor_hint = "scale transform by this factor"; factor_x = 1; factor_y = 1; // pairwise multiply scaled_transform = map2 (map2 multiply) transform.value facs { facs = [[ factor_x, factor_y ], [ 1, 1 ], [ 1, 1 ], [ 1 / factor_x, 1 / factor_y ], [ 1 / factor_x, 1 / factor_y ], [ 1 / factor_x, 1 / factor_y ]]; } } } #separator /* set pixels to 255 - x */ Photographic_negative x = map_unary invert x { invert x = oo_unary_function neg_op x, is_class x = im_invert x, is_image x = 255 - x, is_number x = error (errors.badargs ++ "invert"); neg_op = Operator "photographic_negative" invert Operator_type.ARITHMETIC false; } /* falsecolour a mono image */ Falsecolour x = map_unary falsecolour x; #separator /* adjust brightness and contrast of image x */ Adjust_scale_offset x = map_unary widget x { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; scale = Slider 0.001 255 1; offset = Slider (-128) 128 0; value = clip2fmt image.format (image * scale + offset).value; } } /* adjust gamma of image x */ Adjust_gamma x = map_unary widget x { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; gamma = Slider 0.001 4 1; image_maximum_hint = "Change image_maximum if this is " ++ "not an 8 bit image"; image_maximum = 255; value = clip2fmt image.format gammaed.value { gammaed = (image_maximum / image_maximum ** gamma) * image ** gamma; } } } /* change advisory header fields of image x */ Edit_header x = map_unary widget x { type_names = Image_type.type_names; names = sort (map (extract 0) type_names.value); widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; xres = image.xres; yres = image.yres; xoffset = image.xoffset; yoffset = image.yoffset; type_option = Option "Image type" names pos { name = type_names.lookup 1 0 image.type; pos = index (equal name) names; } value = im_copy_set image.value type xres yres xoffset yoffset { type = type_names.lookup 0 1 names?type_option; } } } #separator /* rotate and scale one image to match another */ Match a b = class Image value { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _vislevel = 3; ap1 = Point_relative a 0.5 0.25; bp1 = Point_relative b 0.5 0.25; ap2 = Point_relative a 0.5 0.75; bp2 = Point_relative b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; value = b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; ap1' = ap1.image_rect; ap2' = ap2.image_rect; bp1' = bp1.image_rect; bp2' = bp2.image_rect; b''' = im_match_linear_search a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2'.left ap2'.top bp2'.left bp2'.top object window, refine = im_match_linear a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2'.left ap2'.top bp2'.left bp2'.top; } } /* make a colour overlay of two mono images */ Overlay a b = class Image value { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _check_all = [ [a.bands == 1 && b.bands == 1, "a.bands == 1 && b.bands == 1"] ] ++ super._check_all; _vislevel = 3; ap1 = Point_relative a 0.5 0.25; bp1 = Point_relative b 0.5 0.25; ap2 = Point_relative a 0.5 0.75; bp2 = Point_relative b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; colour = Option "Colour overlay as" [ "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; value = [(a' ++ b''' ++ black), (a' ++ black ++ b'''), (b''' ++ a' ++ black), (b''' ++ black ++ a'), (black ++ a' ++ b'''), (black ++ b''' ++ a')]?colour { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; ap1' = ap1.image_rect; ap2' = ap2.image_rect; bp1' = bp1.image_rect; bp2' = bp2.image_rect; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1' ap2'; bp2'' = norm bp1' bp2'; b''' = im_match_linear_search a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2''.left ap2''.top bp2''.left bp2''.top; black = image_new a.width a.height a.bands a.format a.coding a.type 0 0 0; } } /* browse through the bands of a multiband image with a slider */ Browse_multiband image = class Image value { _vislevel = 3; band = Slider 0 (image.bands - 1) 0; display = Option "Display as" [ "Grey", "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; value = output { down = (int) band.value; up = (int) (band.value + 1); remainder = band.value - down; a = (image.value ? up) * remainder; b = (image.value ? down) * (1 - remainder); c = image_new image.width image.height 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; output = [ a + b, a ++ b ++ c, a ++ c ++ b, b ++ a ++ c, b ++ c ++ a, c ++ a ++ b, c ++ b ++ a ] ? display; } } ================================================ FILE: share/nip2/compat/7.8/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.8 start_DATA = \ Math.def \ Image.def \ Mosaic.def \ Colour.def \ Resize.def \ Capture.def \ Format.def \ Filter.def \ Morphology.def \ New.def \ Histogram.def \ Print.def \ Rotate.def \ Statistics.def \ X_ray.def \ _convert.def \ _errors.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.8/Math.def ================================================ /* basic arithmetic for objects */ Arithmetic = class { /* add a and b */ Add a b = map_binary add a b; /* subtract b from a */ Subtract a b = map_binary subtract a b; /* multiply a by b */ Multiply a b = map_binary multiply a b; /* divide a by b */ Divide a b = map_binary divide a b; /* remainder after dividing a by b */ Remainder a b = map_binary remainder a b; sep1 = Separator; /* absolute value of x */ Absolute_value x = map_unary abs x; /* like Absolute_value, but treat pixels as vectors */ Absolute_value_vector x = map_unary abs_vec x; /* calculate unit vector for band elements */ Sign x = map_unary sign x; /* multiply by -1 */ Negate x = map_unary unary_minus x; } /* trigonometry operations (all in degrees) */ Trig = class { /* calculate sine x */ Sin x = map_unary sin x; /* calculate cosine x */ Cos x = map_unary cos x; /* calculate tangent x */ Tan x = map_unary tan x; sep1 = Separator; /* calculate arc sine x */ Asin x = map_unary asin x; /* calculate arc cosine x */ Acos x = map_unary acos x; /* calculate arc tangent x */ Atan x = map_unary atan x; sep2 = Separator; /* convert degrees to radians */ Rad x = map_unary rad x; /* convert radians to degrees */ Deg x = map_unary deg x; sep3 = Separator; /* is angle within t degrees of r, mod 360 */ Angle_range t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } /* logarithms and anti-logs */ Log = class { /* calculate e ** x */ Exponential x = map_unary (power e) x; /* log base e of x */ Log_natural x = map_unary log x; sep1 = Separator; /* log base 10 of x */ Log10 x = map_unary log10 x; /* calculate 10 ** x */ Exponential10 x = map_unary (power 10) x; sep2 = Separator; /* calculate x ** y */ Raise_to_power x y = map_binary power x y; } /* operations on complex numbers and images */ Complex = class { /* extract fields from complex */ Complex_extract = class { /* extract real part of complex */ Real in = map_unary re in; /* extract imaginary part of complex */ Imaginary in = map_unary im in; } /* join a and b to make a complex */ Complex_build a b = map_binary comma a b; sep1 = Separator; /* convert real and imag to amplitude and phase */ Polar a = map_unary polar a; /* convert (amplitude, phase) image to rectangular coordinates */ Rectangular x = map_unary rectangular x; sep2 = Separator; /* invert imaginary part */ Conjugate x = map_unary conj x; } /* bitwise boolean operations for integer objects */ Boolean = class { /* bitwise and of a and b */ And a b = map_binary bitwise_and a b; /* bitwise or of a and b */ Or a b = map_binary bitwise_or a b; /* bitwise exclusive or of a and b */ Eor a b = map_binary eor a b; /* invert a */ Not a = map_unary not a; sep1 = Separator; /* shift a right by b bits */ Right_shift a b = map_binary right_shift a b; /* shift a left by b bits */ Left_shift a b = map_binary left_shift a b; sep2 = Separator; /* b where a is non-zero, c elsewhere */ If_then_else a b c = map_trinary if_then_else a b c; /* or the bands of an image together */ Band_or im = foldr1 bitwise_or (bandsplit im); /* and the bands of an image together */ Band_and im = foldr1 bitwise_and (bandsplit im); } /* all comparison operations */ Relational = class { /* find points at which a is equal to b */ Equal a b = map_binary equal a b; /* find points at which a is not equal to b */ Not_equal a b = map_binary not_equal a b; /* find points at which a is greater than b */ More a b = map_binary more a b; /* find points at which a is less than b */ Less a b = map_binary less a b; /* find points at which a is greater than or equal to b */ More_equal a b = map_binary more_equal a b; /* find points at which a is less than or equal to b */ Less_equal a b = map_binary less_equal a b; } /* operations on lists */ List = class { /* take first element of list */ Head x = hd x; /* drop the first element of list */ Tail x = tl x; /* drop the last element of list */ Init x = init x; /* take the last element of list */ Last x = last x; sep1 = Separator; /* reverse order of elements in list */ Reverse x = reverse x; /* sort elements of list into ascending order */ Sort x = sort x; /* remove duplicates from list */ Make_set x = mkset equal x; /* exchange rows and columns in a list of lists */ Transpose_list x = transpose x; /* flatten a list of lists into a single list */ Concat l = concat l; sep2 = Separator; /* find the length of list */ Length x = len x; /* return element n from list (index from zero) */ Subscript n x = n ? x; /* take the first n elements of list x */ Take n x = take n x; /* drop the first n elements of list x */ Drop n x = drop n x; sep3 = Separator; /* join two lists end to end */ Join a b = a ++ b; /* put element a on the front of list x */ Cons a x = a : x; /* join two lists, pairwise */ Zip a b = zip2 a b; } /* various rounding operations */ Round = class { /* smallest integral value not less than x */ Ceil x = map_unary ceil x; /* largest integral value not greater than x */ Floor x = map_unary floor x; /* round to nearest integer */ Rint x = map_unary rint x; } /* forward and reverse fourier transforms */ Fourier = class { /* find fourier transform of image */ Forward a = map_unary (rotquad @ fwfft) a; /* find inverse fourier transform of image */ Reverse a = map_unary (invfft @ rotquad) a; /* rotate quadrants */ Rotate_quadrants a = map_unary rotquad a; } ================================================ FILE: share/nip2/compat/7.8/Morphology.def ================================================ /* Some useful masks. */ _morph_mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; _morph_mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; _morph_mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; _morph_thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; /* dilate x with 8-connected mask */ Dilate8 x = map_unary (dilate _morph_mask8) x; /* dilate x with 4-connected mask */ Dilate4 x = map_unary (dilate _morph_mask4) x; /* erode x with 8-connected mask */ Erode8 x = map_unary (erode _morph_mask8) x; /* erode x with 4-connected mask */ Erode4 x = map_unary (erode _morph_mask4) x; #separator /* open with 8-connected mask */ Open x = map_unary (dilate _morph_mask8 @ erode _morph_mask8) x; /* close with 8-connected mask */ Close x = map_unary (erode _morph_mask8 @ dilate _morph_mask8) x; /* remove single points */ Clean x = map_unary clean x { clean x = x ^ erode _morph_mask1 x; } /* thin once */ Thin x = map_unary thinall x { masks = take 8 (iterate rot45 _morph_thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } #separator /* dilate object x with mask m */ Dilate m x = map_unary (dilate m) x; /* erode object x with mask m */ Erode m x = map_unary (erode m) x; /* dilate mask m with itself n times */ Dilate_multiple m n = (iterate (dilate m) m)?n; /* erode mask m with itself n times */ Erode_multiple m n = (iterate (erode m) m)?n; #separator /* morph with a mask you can edit */ Custom_morphology in = map_unary morph in { morph image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; mask = _morph_mask8; type = Option "Operation" ["Erode", "Dilate"] 1; hint_apply_n_times = "Number of times to apply mask:"; apply_n_times = 1; border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 xoff yoff image.width image.height, border = image' { fatmask = (iterate (dilate mask) mask) ? (apply_n_times - 1); image' = im_erode_raw image.value fatmask, type.value == 0 = im_dilate_raw image.value fatmask; xoff = (fatmask.width + 1) / 2; yoff = (fatmask.height + 1) / 2; } } } /* search in from the edges of an image for the first non-zero pixel */ Find_profile in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; value = image_set_type Image_type.HISTOGRAM [ im_profile image.value 0, rot270 (im_profile image.value 1), im_profile (flipud image.value) 0, rot270 (im_profile (fliplr image.value) 1) ]?edge; } } /* count the average number of black-to-white transitions across an image */ Count_lines in = map_unary widget in { widget image = class Real value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; value = im_cntlines image.value edge.value; } } ================================================ FILE: share/nip2/compat/7.8/Mosaic.def ================================================ /* Check and group a point list by image. */ _mosaic_sort_test l = error "mosaic: not all points", !is_listof (is_instanceof "Point") l = error "mosaic: points not on two images", len images != 2 = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = land (map (equal (hd l)) (tl l)); get_format x = x.image.format; get_coding x = x.image.coding; // all the different images get_image x = x.image; images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = p.image === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to get * above before below. */ _mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = (a?0).left > (b?0).left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to get * left before right. */ _mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = (a?0).top > (b?0).top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ _mosaic_sort fn = concat @ transpose @ fn @ _mosaic_sort_test; /* translate and blend two images together left/right or up/down */ Mosaic_translate = class { _check_ab_args a b = [ [a, "a", check_Point], [b, "b", check_Point] ]; // shortcut to prefs _prefs = Workspaces.Preferences; _search_area = _prefs.MOSAIC_WINDOW_SIZE; _object_size = _prefs.MOSAIC_OBJECT_SIZE; _blend_width = Slider 0 100 _prefs.MOSAIC_MAX_BLEND_WIDTH; _refine = Toggle "Refine selected tie-points" _prefs.MOSAIC_REFINE; /* translate and blend two images left/right */ Left_right a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_lrmosaic a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_lrmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_lr [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } /* translate and blend two images top/bottom */ Top_bottom a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_tbmosaic a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_tbmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_tb [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } } /* forcibly translate and blend two images together left/right or up/down */ Mosaic_force = class { _check_ab_args a b = [ [a, "a", check_Point], [b, "b", check_Point] ]; // shortcut to prefs _prefs = Workspaces.Preferences; _blend_width = Slider 0 100 _prefs.MOSAIC_MAX_BLEND_WIDTH; /* forcibly translate and blend two images left/right */ Left_right a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; value = im_lrmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_lr [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } /* forcibly translate and blend two images top/bottom */ Top_bottom a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; value = im_tbmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_tb [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } } /* translate, rotate, scale and blend two images together left/right or up/down */ Mosaic_affine = class { _check_abcd_args a b c d = [ [a, "a", check_Point], [b, "b", check_Point], [c, "c", check_Point], [d, "d", check_Point] ]; // shortcut to prefs _prefs = Workspaces.Preferences; _search_area = _prefs.MOSAIC_WINDOW_SIZE; _object_size = _prefs.MOSAIC_OBJECT_SIZE; _blend_width = Slider 0 100 _prefs.MOSAIC_MAX_BLEND_WIDTH; _refine = Toggle "Refine selected tie-points" _prefs.MOSAIC_REFINE; /* translate, rotate, scale and blend two images left/right */ Left_right a b c d = class Image value { _check_args = _check_abcd_args a b c d ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_lrmosaic1 a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_lrmerge1 a'.image.value b'.image.value ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top blend_width.value { sorted = _mosaic_sort _mosaic_sort_lr [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; ra' = a'.image_rect; rb' = b'.image_rect; rc' = c'.image_rect; rd' = d'.image_rect; } } /* translate, rotate, scale and blend two images top/bottom */ Top_bottom a b c d = class Image value { _check_args = _check_abcd_args a b c d ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_tbmosaic1 a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_tbmerge1 a'.image.value b'.image.value ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top blend_width.value { sorted = _mosaic_sort _mosaic_sort_tb [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; ra' = a'.image_rect; rb' = b'.image_rect; rc' = c'.image_rect; rd' = d'.image_rect; } } } #separator /* disassemble mosaic, adjust brightnesses to match, and reassemble */ Mosaic_balance x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (errors.badargs ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } /* brighten along a left/right or up/down axis */ Tilt_brightness = class { /* brighten along a left/right axis */ Left_right x = map_unary tilt_lr x { tilt_lr image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; tilt = Slider (-1) 1 0; value = final { ramp = im_fgrey image.width image.height; ramp' = bandjoin (map (const ramp) [1..image.bands]); scale = (ramp' - 0.5) * tilt + 1; final = image.value * scale; } } } /* brighten along a top/bottom axis */ Top_bottom x = map_unary tilt_tb x { tilt_tb image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; tilt = Slider (-1) 1 0; value = final { ramp = rot90 (im_fgrey image.height image.width); ramp' = bandjoin (map (const ramp) [1..image.bands]); scale = (ramp' - 0.5) * tilt + 1; final = image.value * scale; } } } } #separator /* disassemble mosaic, substitute different image files, and reassemble */ Mosaic_rebuild x = map_unary remosaic x { remosaic image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; old_hint = "For the name of each file making up the mosaic, " ++ "exchange this string:"; old = "foo"; new_hint = "for this string:"; new = "bar"; value = im_remosaic image.value old new; } } ================================================ FILE: share/nip2/compat/7.8/New.def ================================================ /* make a new blank image */ New_image = widget { type_names = Image_type.type_names; names = sort (map (extract 0) type_names.value); default_type_name = type_names.lookup 1 0 Image_type.MULTIBAND; default_type_pos = index (equal default_type_name) names; widget = class Image value { width = 64; height = 64; bands = 1; format_option = Option "Image format" [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ] 0; type_option = Option "Image type" names default_type_pos; value = image_new width height bands format Image_coding.NOCODING type 0 0 0 { format = format_option.value; type = type_names.lookup 0 1 names?type_option; } } } /* pick a colour ... any colour space */ New_colour = widget "Lab" [50,0,0] { widget default_colour value = class Colour colour_space value { _colourspaces = [ "XYZ", "Yxy", "Lab", "LCh", "UCS", "RGB", "sRGB" ]; colour_space_option = Option "Colour space" _colourspaces (index (equal default_colour) _colourspaces); colour_space = colour_space_option.labels? colour_space_option.value; Colour_edit colour_space value = widget colour_space value; } } /* make a slider */ New_slider = Slider 0 255 128; /* make a toggle widget */ New_toggle = Toggle "untitled" false; /* make an option widget */ New_option = Option "untitled" ["option0", "option1"] 0; /* make a string entry widget */ New_string = String "Enter a string" "sample text"; /* make a number entry widget */ New_number = Number "Enter a number" 42; /* make a file chooser */ New_filename = Filename "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; /* make a matrix */ New_matrix = class { /* make a plain matrix */ Plain = Matrix (identity_matrix 3); /* make a convolution matrix */ Convolution = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; /* make a recombination matrix */ Recombination = Matrix_rec (identity_matrix 3); /* make a morphology matrix */ Morphology = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } /* make a mark on an image */ New_mark = class { /* mark a region on an image */ Region image = scope.Region_relative image 0.25 0.25 0.5 0.5; /* mark a point on an image */ Point image = scope.Point_relative image 0.5 0.5; /* mark an arrow on an image */ Arrow image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; /* mark a horizontal guide on an image */ HGuide image = scope.HGuide_relative image 0.5; /* mark a vertical guide on an image */ VGuide image = scope.VGuide_relative image 0.5; } #separator /* make a spatial response pattern image */ New_eye = class Image value { width = 64; height = 64; factor = Slider 0.001 1 0.2; value = im_eye width height factor.value; } /* make a zone plate image */ New_zone_plate = class Image value { size = 64; value = im_zone size; } /* make a grey ramp image */ New_grey = class Image value { width = 64; height = 64; orientation = Option "" ["Horizontal", "Vertical"] 0; value = [im_grey width height, im_rot90 (im_grey height width)]?orientation; } /* make a two band image whose pixel values are their coordinates */ New_xy = class Image value { width = 64; height = 64; value = make_xy width height; } /* make a new image of gaussian noise */ New_gauss_noise = class Image value { size = 64; mean = Slider 0 255 128; deviation = Slider 0 128 50; value = im_gaussnoise size size mean.value deviation.value; } /* make a 2d fractal image */ New_fractal = class Image value { size = 64; dimension = Slider 2.001 2.999 2.001; value = im_fractsurf size dimension.value; } /* make a CRT calibration chart */ New_CRT_test_chart = class Image value { brightness = Slider 0 255 200; patch_size = 32; value = imagearray_assemble 0 0 [[green, red], [blue, white]] { black = image_new patch_size patch_size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } /* make a frequency test chart */ New_frequency_test_chart = class Image value { width = 64; strip_height = 10; wavelengths = [64, 32, 16, 8, 4, 2]; value = join.value { freq_slice wave = Image (sin ((im_fgrey width strip_height) * 360 * width / wave) > 0); strips = map freq_slice wavelengths; join = foldl1 Join.Top_bottom strips; } } /* make a checkerboard pattern */ New_checkerboard = class Image value { width = 64; height = 64; horizontal_patch_size = 8; vertical_patch_size = 8; horizontal_patch_offset = 0; vertical_patch_offset = 0; value = xstripes ^ ystripes { pixels = make_xy width height; xpixels = pixels?0 + horizontal_patch_offset; ypixels = pixels?1 + vertical_patch_offset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels horizontal_patch_size; ystripes = make_stripe ypixels vertical_patch_size; } } /* make a grid pattern */ New_grid = class Image value { width = 64; height = 64; horizontal_line_spacing = 8; vertical_line_spacing = 8; line_thickness = 1; horizontal_grid_offset = 0; vertical_grid_offset = 0; value = xstripes | ystripes { pixels = make_xy width height; xpixels = pixels?0 + horizontal_grid_offset; ypixels = pixels?1 + vertical_grid_offset; make_stripe pix swidth = pix % swidth < line_thickness; xstripes = make_stripe xpixels horizontal_line_spacing; ystripes = make_stripe ypixels vertical_line_spacing; } } #separator /* make a gaussian matrix */ New_gauss_matrix = class Matrix_vips _mask.value _mask.scale _mask.offset _mask.filename _mask.display { sigma = Slider 0.001 10 1; min_amplitude = Slider 0 1 0.2; integer = Toggle "Integer" false; _mask = fn sigma.value min_amplitude.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } /* make a laplacian of a gaussian mask */ New_log_matrix = class Matrix_vips _mask.value _mask.scale _mask.offset _mask.filename _mask.display { sigma = Slider 0.001 10 1.5; min_amplitude = Slider 0 1 0.1; integer = Toggle "Integer" false; _mask = fn sigma.value min_amplitude.value { fn = im_log_imask, integer = im_log_dmask; } } #separator /* make the mask images for various ideal fourier filters */ New_ideal = class { /* make a mask image for an ideal highpass/lowpass fourier filter */ High_low = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value 0 0 0 0; } } /* make a mask image for an ideal ring pass/reject fourier filter */ Ring = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 6; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value ring_width.value 0 0 0; } } /* make a mask image for an ideal band pass/reject fourier filter */ Band = class Image value { size = 64; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 12; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; } } } /* various Gaussian fourier filters */ New_gaussian = class { /* make a mask image for a gaussian highpass/lowpass fourier filter */ High_low = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 4; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value amplitude_cutoff.value 0 0 0; } } /* make a mask image for a gaussian ring pass/reject fourier filter */ Ring = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 10; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; } } /* make a mask image for a gaussian band pass/reject fourier filter */ Band = class Image value { size = 64; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 16; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; } } } /* various Butterworth fourier filters */ New_butterworth = class { /* make a mask image for a Butterworth highpass/lowpass fourier filter */ High_low = class Image value { size = 64; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 2; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type order.value frequency_cutoff.value amplitude_cutoff.value 0 0; } } /* make a mask image for a Butterworth ring pass/reject fourier filter */ Ring = class Image value { size = 64; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 8; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; } } /* make a mask image for a Butterworth band pass/reject fourier filter */ Band = class Image value { size = 64; order = Slider 1 10 2; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 14; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; } } } #separator /* make a slice through CIELAB space */ New_CIELAB_slice = class Image value { size = 64; L = Slider 0 100 50; value = lab_slice size L.value; } /* pick a colour in LAB space */ New_LAB_colour = widget "Lab" [50, 0, 0] { // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range); ab2xy a = (a * (size / range)); widget space default_value = class Colour space value { L = default_value?0; a = default_value?1; b = default_value?2; lightness = Slider 0 100 L; ab_slice = Image (lab_slice size lightness.value); point = Point ab_slice (ab2xy a) (ab2xy b); value = [lightness.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } ================================================ FILE: share/nip2/compat/7.8/Print.def ================================================ /* cored sharpen of L only in LAB image ... tuned for typical printers */ Sharpen_for_print in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "300 dpi", "150 dpi", "75 dpi" ] 0; // sharpen params for 300, 150 and 75 dpi // just change the size of the area we search _param_table = [ [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] ]; _params = _param_table?target_dpi; value = im_sharpen (colour_transform_to Image_type.LABQ in.value) _params?0 _params?1 _params?2 _params?3 _params?4 _params?5; } } /* adjust tone curve on L* */ Tone_for_print in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; black = Slider 0 100 0; white = Slider 0 100 100; shadow_point = Slider 0.1 0.3 0.2; mid_point = Slider 0.4 0.6 0.5; highlight_point = Slider 0.7 0.9 0.8; shadow_adjust = Slider (-15) 15 0; mid_adjust = Slider (-30) 30 0; highlight_adjust = Slider (-15) 15 0; preview_curve = Image (im_tone_build black.value white.value shadow_point.value mid_point.value highlight_point.value shadow_adjust.value mid_adjust.value highlight_adjust.value); value = im_tone_map (colour_transform_to Image_type.LABQ in.value) preview_curve.value; } } /* morph image colours in LAB space ... useful for tweaking colour for print */ Morph_for_print in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; L_scale = 1.15; L_offset = -4.2; ab_scale = Slider 1 1.5 1.15; a_offset = Slider (-10) 10 0; b_offset = Slider (-10) 10 5; grey_correction = Matrix_con 1 0 [ [5, 5, -1 ], [10, 4, -1 ], [15, 2, -1 ], [20, 1, 1 ], [25, 1, 2 ], [30, 0, 1 ], [35, 0, 1 ], [40, 0, 1 ], [45, 0, 1 ], [50, 0, 1 ], [55, 0, 0 ], [99, 0, 0 ] ]; value = im_lab_morph in.value (Vector [0, a_offset.value, b_offset.value] + grey_correction) L_offset L_scale ab_scale.value ab_scale.value; } } #separator _sample_print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; _sample_monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; _render_intents = Option "Render intent" [ "Perceptual", "Relative", "Saturation", "Absolute" ] 3; /* transform from PCS to device space */ ICC_export in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; profile = Filename _sample_print_profile; intent = _render_intents; depth = Option "Output depth" ["8 bit", "16 bit"] 0; value = im_icc_export_depth in.value [8, 16]?depth (expand profile.value) intent.value; } } /* transform from device space to PCS */ ICC_import in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _check_all = [ [in.bands == 3 || in.bands == 4, "in.bands == 3 || in.bands == 4" ] ] ++ super._check_all; _vislevel = 3; profile = Filename _sample_monitor_profile, in.bands == 3 = Filename _sample_print_profile; intent = _render_intents; value = im_icc_import in.value (expand profile.value) intent.value; } } /* transform between two device spaces */ ICC_transform in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; in_profile = Filename _sample_monitor_profile; out_profile = Filename _sample_print_profile; intent = _render_intents; value = im_icc_transform in.value (expand in_profile.value) (expand out_profile.value) intent.value; } } /* transform from absolute to relative colorimetry */ ICC_ac2rc in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; profile = Filename _sample_print_profile; value = im_icc_ac2rc in.value (expand profile.value); } } #separator /* convert between XYZ and Lab for D50 */ D50XYZ_to_D50Lab in = map_unary (colour_unary im_D50XYZ2Lab) in; /* convert between XYZ and Lab for D50 */ D50Lab_to_D50XYZ in = map_unary (colour_unary im_D50Lab2XYZ) in; #separator /* add an editable drop shadow to an image */ Drop_shadow x = map_unary shadow x { shadow image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; shadow_width = Slider 0 50 5; shadow_height = Slider 0 50 5; shadow_softness = Slider 0 20 5; use_mask = Toggle "Use mask to make shadow" false; mask_image = foldr1 bitwise_and (bandsplit (image > 128)); background_colour = 255; shadow_colour = 128; value = final { blur_size = shadow_softness.value * 2 + 1; // matrix we blur with to soften shadows mask_g = im_gauss_imask (blur_size / 3) 0.2; mask_g_line = mask_g.value ? (mask_g.height / 2); mask_g_sum = foldr1 add mask_g_line; blur_matrix = Matrix_con mask_g_sum 0 [mask_g_line]; mask_size = mask_g.width; // size of final image we build final_width = image.width + 2 * mask_size + shadow_width.value; final_height = image.height + 2 * mask_size + shadow_height.value; // make a plain image mk_background colour = image_new final_width final_height image.bands image.format Image_coding.NOCODING image.type colour 0 0; // make a mask image ... place at (x,y) in the final // image mk_mask x y = im_insert black mask_image.value x y, use_mask = im_insert black white x y { black = image_new final_width final_height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 0 0 0; white = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; } // make the shadow mask image ... offset mask and // soften shadow_mask = mk_mask (mask_size + shadow_width.value) (mask_size + shadow_height.value); shadow_mask' = im_convsep shadow_mask blur_matrix; // make underlay ... use shadow mask to blend between // background colour and shadow colour background = mk_background background_colour; shadow = mk_background shadow_colour; underlay = im_blend shadow_mask' shadow background; // overlay ... place image at final position overlay = mk_background 0; overlay' = im_insert overlay image.value mask_size mask_size; // overlay mask overlay_mask = mk_mask mask_size mask_size; final = if overlay_mask then overlay' else underlay; } } } ================================================ FILE: share/nip2/compat/7.8/Resize.def ================================================ _resize_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.bilinear; /* resize image x by any scale factor */ Resize_image x = map_unary widget x { widget image = class Image value { _vislevel = 3; factor = 1; interp = _resize_interp; value = resize factor factor interp.value image.value; } } /* resize image with separate x/y factors */ Resize_xy_image x = map_unary widget x { widget image = class Image value { _vislevel = 3; xfactor = 1; yfactor = 1; interp = _resize_interp; value = resize xfactor yfactor interp.value image.value; } } /* place image x in a larger piece of image */ Resize_canvas x = map_unary widget x { widget image = class Image value { _vislevel = 3; new_image_width = image.width; new_image_height = image.height; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east" ] 4; fill = Option "Fill background with" [ "White", "Black" ] 0; value = im_insert_noexpand background image.value xp yp { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; background_colour = image_white image, fill == 0 = Vector (map (const 0) [1 .. bands]); // placement vectors ... left, centre, right xposv = [0, new_image_width / 2 - width / 2, new_image_width - width]; yposv = [0, new_image_height / 2 - height / 2, new_image_height - height]; xp = xposv?((int) (position % 3)); yp = yposv?((int) (position / 3)); background = image_new new_image_width new_image_height bands format coding type background_colour 0 0; } } } #separator /* resize image x so that the shortest axis is a certain size */ Shrink_to = class { _shrink_width default_size image = class Image value { size = default_size; interp = _resize_interp; value = resize factor factor interp.value image.value { xfac = size / image.width; yfac = size / image.height; factor = max_pair xfac yfac; } } /* shrink minimum dimension to 400 pixels */ Quicklook x = map_unary (_shrink_width 400) x; /* shrink minimum dimension to 64 pixels */ Icon x = map_unary (_shrink_width 64) x; } ================================================ FILE: share/nip2/compat/7.8/Rotate.def ================================================ /* rotate images and matricies by fixed angles */ Rotate_fixed = class { /* rotate image clockwise in 90 degree increments */ _rotate_widget default a = map_unary widget a { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; value = [ image.value, rot90 image.value, rot180 image.value, rot270 image.value ] ? angle; } } /* clockwise rotate by 90 degrees */ R90 x = _rotate_widget 1 x; /* rotate by 180 degrees */ R180 x = _rotate_widget 2 x; /* clockwise rotate by 270 degrees */ R270 x = _rotate_widget 3 x; /* rotate by 45 degrees ... square, odd-length-sides, matrices only */ R45 x = map_unary rot45 x; } /* rotate image anticlockwise by any angle */ Rotate_free a = map_unary widget a { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; angle = Slider 0 360 0; value = rotate angle image.value; } } #separator /* mirror left/right or up/down */ Flip = class { /* mirror object up/down */ Up_down x = map_unary flipud x; /* mirror object left/right */ Left_right x = map_unary fliplr x; } /* swap rows and columns */ Transpose x = map_unary transpose x; #separator /* smallest rotate that gets arrow vertical or horizontal */ Straighten_arrow x = map_unary straighten x { straighten arrow = rotate angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } ================================================ FILE: share/nip2/compat/7.8/Statistics.def ================================================ /* mean value of object */ Mean a = map_unary mean a; /* standard deviation of object */ Deviation a = map_unary deviation a; /* calculate a large set of stats on an object */ Stats a = map_unary stats a; #separator /* maximum of an object */ Max a = map_unary max a; /* minimum of an object */ Min a = map_unary min a; /* return complex number (max, min) */ Maxmin a = map_unary maxmin a { maxmin x = (Max x, Min x); } /* position of maximum in an image */ Maximum_position a = map_unary maxpos a; /* position of minimum in an image */ Minimum_position a = map_unary minpos a; #separator /* count the number of non-zeros in an image */ Count_set a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } /* count the number of zeros in an image */ Count_clear a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } #separator /* plot a scatter graph of a list of [x,y] coordinates */ Plot_scatter x //= map_unary widget x = widget x { widget data = class Image value { _check_args = [ [data, "data", check_xy_list] ] ++ super._check_args; width = 512; height = 512; plot_colour = Colour "Lab" [80, -80, 80]; xmin = foldr1 min_pair (map (extract 0) data); xmax = foldr1 max_pair (map (extract 0) data); ymin = foldr1 min_pair (map (extract 1) data); ymax = foldr1 max_pair (map (extract 1) data); axies = Toggle "Draw axies" true; mark = Point this x y { p = _to_image data?0; x = p?0; y = p?1; } mark_position_hint = "mark is at position:"; mark_position = _from_image [mark.left, mark.top]; // geometry _xrange = xmax - xmin; _yrange = ymax - ymin; _xscale = width / _xrange; _yscale = height / _yrange; // map an [x,y] point into the image coordinates _to_image p = [(p?0 - xmin) * _xscale, height - (p?1 - ymin) * _yscale]; // map an [x,y] point from image cods back to real cods _from_image p = [p?0 / _xscale + xmin, (height - p?1) / _yscale + ymin]; value = foldr plot background' data { plot_image_new width height pixel = image_new width height 3 Image_format.FLOAT Image_coding.NOCODING (Image_type.colour_spaces.lookup 0 1 plot_colour.colour_space) pixel 0 0; // background background = plot_image_new width height 0; // mark we plot mark_width = max_pair 1 (width / 100); mark_height = max_pair 1 (height / 100); mark = plot_image_new mark_width mark_height plot_colour; // draw axies on background background' = drawxy, axies = background { // axies xaxis = plot_image_new width 1 (Colour "Lab" [100, 0, 0]); yaxis = plot_image_new 1 height (Colour "Lab" [100, 0, 0]); origin = _to_image [0, 0]; drawx = im_insert_noexpand background xaxis 0 origin?1; drawxy = im_insert_noexpand drawx yaxis origin?0 0; } // plot a single point on an image plot p im = im_insert_noexpand im mark (x - mark_width / 2) (y - mark_height / 2) { p' = _to_image p; x = p'?0; y = p'?1; } } } } ================================================ FILE: share/nip2/compat/7.8/X_ray.def ================================================ /* replace dark or light section of im1 with section from im2 */ Replace_area im1 im2 = class Image value { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ] ++ super._check_args; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.1 0.1 0.1 0.1; /* Point on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Point im2 (im2.width * 0.1 - im2.xoffset) (im2.height * 0.1 - im2.yoffset); _r2 = Region im2 p2.left p2.top r1.width r1.height; _mask = [r1 <= Options.scale_cutoff, r1 >= Options.scale_cutoff] ? Options.control; mask = _mask?0; Options = option_1, format < 4 = option_2 { format = im1.format; option_1 = class { _vislevel = 3; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ control = Option "Removing a" [ "Dark Area", "Light Area" ] 1; /* Used to select the area to be replaced. */ scale_cutoff = Slider 0.01 mx (mx / 2) { mx = Image_format.maxval im1.format; } /* Option toggle how the levels in the replacment area are calculated. * Replacement with gaussian noise uses the scale&offset balancing. */ process = Option "Use" [ "Scale&Offset Balancing", "Gaussian noise replacement", "Histogram Balancing" ] 0; /* This allows the function to be paused. */ pause = Toggle "Pause function to allow easy adjustment of region r1." true; } option_2 = class { _vislevel = 3; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ control = Option "Removing a" [ "Dark Area", "Light Area" ] 1; /* Used to select the area to be replaced. */ scale_cutoff = Slider 0.01 mx (mx / 2) { /* the below function can not cope with floats * and don't need massive range for 32-bit ints so * will just define the max as for a 16 bit unsigned. */ mx = Image_format.maxval 2; //im1.format; } /* Option toggle how the levels in the replacment area are calculated. * Replacement with gaussian noise uses the scale&offset balancing. */ process = Option "Use" [ "Scale&Offset Balancing", "Gaussian noise replacement" ] 0; /* This allows the function to be paused. */ pause = Toggle "Pause function to allow easy adjustment of region r1." true; } } value = im1.value, Options.pause = im_insert im1.value patch r1.left r1.top { patch = _so_balance mask r1.value _r2.value false, Options.process == 0; = _so_balance mask r1.value _r2.value true, Options.process == 1; = _hist_balance_2 mask r1.value _r2.value; } }; #separator /* Balance the effect of secondary structure on an X-ray image * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference * masks, where the masks are white on a black background. Then simplifys * the original X-ray to reduce interference from stretchers cradles etc. */ Balance_areas im_in m_control m_list = class Image value { _vislevel = 3; _format = im_in.format; Options = option_1, format < 4 = option_2 { format = im_in.format; option_1 = class { _vislevel = 3; pause = Toggle "Pause Process." true; blur = Slider 0 5 0; _blur = rint blur.value; option = Toggle "Use Scale&Offset Balancing rather than Histogram." true; _option = option; } option_2 = class { _vislevel = 3; pause = Toggle "Pause Process." true; blur = Slider 0 5 0; _blur = rint blur.value; _option = true; } } _control_im = _section_select2 im_in m_control; _control_values = _so_values _control_im; /* blur mask over a set number of pixels then histogram match an area * of the original image defined by m_current to the _control_im * then blend the matched area back into im_in. */ process m_current im_start = im_out { alternative = false; bl_mask = _mask_blur_2 m_current Options._blur; scaled_im = _so_convert _control_values im_start m_current, Options._option == true = _hist_convert_2 _control_im im_start m_current; fmt_im = clip2fmt im_start.format scaled_im; blended_im = im_blend bl_mask scaled_im.value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } value = im_in.value, Options.pause == true = (foldr process im_in m_list).value; }; _so_balance mask im1 im2 gauss = result { /* Extract the undamaged areas. */ im1' = if !mask then Image im1 else 0; im2' = if !mask then Image im2 else 0; /* Find the non_zero means of the undamaged areas. */ m1 = _mean_fn im1'; m2 = _mean_fn im2'; im1_mn = im1' - m1; im2_mn = im2' - m2; scale = (max im1_mn)/(max im2_mn); im2_corrected_a = ((im2 - m2) * scale) + m1; im2_corrected_b = clip2fmt im1'.format im2_corrected_a; /* Option to convert replacement image to scaled gaussian noise */ im2_corrected = im2_corrected_b, gauss == false = _gauss_noise im2_corrected_b; /* Blur mask. */ mask' = _mask_blur_2 mask 5; //mask' = _feather_mask_2 5 mask.value; /* Blend im2 into im1. */ result = im_blend mask' im2_corrected im1; }; /* make a new image of gaussian noise */ _gauss_noise im2 = value { i = Image (im2); width = i.width; height = i.height; mean = Mean i; deviation = Deviation i; noise = im_gaussnoise width height mean deviation; value = clip2fmt i.format noise; }; /* Dilate and blur a mask by a number of pixels. * *_feather_mask_2 pixels mask * = im_convsep (dilate dilate_matrix mask) blur_matrix *{ * dilate_matrix = (iterate (dilate _morph_mask8) _morph_mask8) ? pixels; * blur_matrix = Matrix_con pixels 0 [(map (const 1) [1 .. pixels])]; *}; */ /* Mask is 255 to indicate parts of im1 which are damaged: replace these bits * with the corresponding parts of im2. * * Match the histograms of the two images to hide grey-level differences, be * careful to only consider undamaged sections. * * Feather the edges of the blend to hide the join. * * mask is an Image class. im1, im2 and result are vips images. */ _hist_balance_2 mask im1 im2 = result { format = im_header_int "BandFmt" im1; bands = im_header_int "Bands" im1; /* checks for 8 or 16 bit signed and converts to unsigned */ force_unsigned i = clip2fmt Image_format.UCHAR (i + 128), format == Image_format.CHAR = clip2fmt Image_format.USHORT (i + 32768), format == Image_format.SHORT = i; /* undo any force_unsigned */ format_restore i = clip2fmt Image_format.CHAR (i - 128), format == Image_format.CHAR = clip2fmt Image_format.SHORT (i - 32768), format == Image_format.SHORT = i; /* Find histogram and then zap the zero column (0 == background). For * 16-bit images, the histogram can be smaller than 65535 .... expand * up to full size. */ build_hist i = im_insert big_black h' 0 0 { max_value = Image_format.maxval i.format; h = hist_find i.value; black_1 = image_new 1 1 i.bands Image_format.UINT Image_coding.NOCODING i.type 0 0 0; big_black = image_new max_value 1 i.bands Image_format.UINT Image_coding.NOCODING i.type 0 0 0; h' = im_insert h black_1 0 0; } /* We can't get hists of signed images :-( go unsigned if we have to. */ im1' = force_unsigned im1; im2' = force_unsigned im2; /* Extract the undamaged areas. */ reference = if !mask then Image im1' else 0; secondary = if !mask then Image im2' else 0; /* Find the hists of the undamaged areas. */ h1 = build_hist reference; h2 = build_hist secondary; /* Match greylevels of im2 to match im1. */ im2'' = hist_map (hist_match h1 h2) im2'; /* Feather mask. */ mask' = _mask_blur_2 mask 5; //mask' = _feather_mask_2 5 mask.value; /* Blend im2 into im1. */ im1'' = im_blend mask' im2'' im1'; /* Undo any signed/unsigned nonsense. */ result = format_restore im1''; }; // Blurs the edges of an 8 bit mask, over a given number of pixels. _mask_blur_2 mask pixel = value { pixels = 1, pixel == 0 = pixel; black = im_black (mask.width + (2*pixels)) (mask.height + (2*pixels)) 1; new_mask = Image (im_insert black mask.value pixels pixels); blur_matrix = Matrix_con pixels 0 [(map (const 1) [1 .. pixels])]; im_blur = im_convsep new_mask.value blur_matrix; left = pixels; top = pixels; width = mask.width; height = mask.height; value = im_extract_area im_blur left top width height; }; _so_values im = result { mean_of_im = _mean_fn im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; _so_convert con_values im mask = result { im' = _section_select2 im mask; im_values = _so_values im'; mean_of_con = con_values?0; mean_of_im = im_values?0; max_of_con = con_values?1; max_of_im = im_values?1; scale = (max_of_con)/(max_of_im); im_convert = ((im - mean_of_im) * scale) + mean_of_con; result = clip2fmt im.format im_convert; }; /* Convert the histogram of part of image im_a, depending on the mask im_m, * to match the histogram of im_c. */ _hist_convert_2 control_im im adjust_mask = im_final { max_value = Image_format.maxval im.format; adjust_im = _section_select2 im adjust_mask; hist_c = hist_fn control_im; hist_a = hist_fn adjust_im; /* Find histogram and then edit out any information related to parts * of the image with value 0. Ensure that the histogram represents * the full scale for the appropriate format. Output corrected * histograms for the correct parts of the image. */ hist_fn im_in = output { hist_1 = hist_find im_in; black_1 = clip2fmt hist_1.format (im_black 1 1 1); hist_2 = im_insert hist_1.value black_1 0 0; black_2 = clip2fmt hist_1.format (im_black max_value 1 1); output = im_insert black_2 hist_2 0 0; } im_final = hist_map (hist_match hist_a hist_c) im; }; //------------------------------------------------------------------------------- /*Returns a section of im, defined by a mask, on a black background*/ _section_select2 im_in mask = output { output = clip2fmt im_in.format (im_black im_in.width im_in.height 1), mask == 0 = im_in; }; _mean_fn im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); } ================================================ FILE: share/nip2/compat/7.8/_convert.def ================================================ /* Convert an image ... just set the Type field. */ image_set_type type in = im_copy_set in type (im_header_double "Xres" in) (im_header_double "Yres" in) (im_header_int "Xoffset" in) (im_header_int "Yoffset" in); /* Convert an image ... just set origin */ image_set_origin xoff yoff in = im_copy_set in (im_header_int "Type" in) (im_header_double "Xres" in) (im_header_double "Yres" in) xoff yoff; /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = oo_unary_function to_matrix_op x, is_class x = tom x, is_real x || is_image x = error (errors.badargs ++ "to_matrix") { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (errors.badargs ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i && bands == 1 = (im_vips2mask ((double) i'')).value, is_image i && bands == 3 && width == 1 = error errors.not1band3band { width = im_header_int "Xsize" i; bands = im_header_int "Bands" i; split = bandsplit i; i' = im_insert (split?0) (split?1) 1 0; i'' = im_insert i' (split?2) 2 0; } } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_instanceof "Colour" x = oo_unary_function to_image_op x, is_class x = toi x, is_real x || is_image x = error (errors.badargs ++ "to_image") { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (errors.badargs ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } /* Try to make a real. */ to_real x = to_real x.value, is_class x = x, is_real x = abs x, is_complex x = error (errors.badargs ++ "to_real"); /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error "parse_c: not a digit", ! is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error "parse_number: badly formed number", len parts != 2 = sign * n { parts = splitpl [ member "+-", is_digit ] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, len parts != 4 = (ipart + fpart) * 10 ** exp { err = error "parse_float: badly formed number"; parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10**(len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Print integer as hex. */ print_hex i = "0", chars == [] = "0x" ++ reverse chars { digits = takewhile (not_equal 0) (map (bitwise_and 0xf) (iterate (converse right_shift 4) i)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'a' + (x - 10)); } /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[ 0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [ 0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { // divide by D50 white point xyz' = xyz / Vector [96.4250, 100.0, 82.4680]; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * Vector [95.0470, 100.0, 108.8827]; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { // divide by D65 white point xyz' = xyz / Vector [95.0470, 100.0, 108.8827]; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D50 xyz''' = xyz'' * Vector [96.4250, 100.0, 82.4680]; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz 96.4250 100 82.4680; /* Convert D50 Lab to XYZ. */ im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab 96.4250 100 82.4680; /* ... and mono conversions */ im_sRGB2mono in = clip2fmt (im_header_int "BandFmt" in) (im_recomb in (Matrix [[.3, .6, .1]])); im_mono2sRGB in = (unsigned char) (in ++ in ++ in); /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = fn x, is_image x = error (errors.badargs ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = fn x, is_image x = error (errors.badargs ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (errors.badargs ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (errors.badargs ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, LAB, im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, sRGB, im_mono2sRGB], [B_W, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy], [XYZ, LAB, im_XYZ2Lab], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS], [XYZ, RGB, im_XYZ2disp], [XYZ, sRGB, im_XYZ2sRGB], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ], [YXY, XYZ, im_Yxy2XYZ], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ], [LAB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ], [LAB, XYZ, im_Lab2XYZ], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ], [LAB, LAB, image_set_type LAB], [LAB, LCH, im_Lab2LCh], [LAB, UCS, im_Lab2UCS], [LAB, RGB, im_Lab2disp], [LAB, sRGB, im_XYZ2sRGB @ im_Lab2XYZ], [LAB, LABQ, im_Lab2LabQ], [LAB, LABS, im_Lab2LabS], [LCH, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LCh2Lab], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab], [LCH, YXY, im_XYZ2Yxy @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LCh2Lab], [LCH, LAB, im_LCh2Lab], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS], [LCH, RGB, im_Lab2disp @ im_LCh2Lab], [LCH, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LCh2Lab], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ], [UCS, XYZ, im_UCS2XYZ], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab], [UCS, LAB, im_UCS2Lab], [UCS, LCH, im_UCS2LCh], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab], [UCS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_UCS2Lab], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [LABQ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error ("unable to convert " ++ from_name ++ " to " ++ to_name) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; from_name = Image_type.type_names.lookup 1 0 from; to_name = Image_type.type_names.lookup 1 0 to; } /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_instanceof "Image" x || is_instanceof "Arrow" x || is_instanceof "Colour" x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_instanceof "Image" x = get_type_im x.image.value, is_instanceof "Arrow" x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_instanceof "Colour" x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.B_W, bands == 1 = type, bands == 3 && is_colorimetric = Image_type.MULTIBAND, bands != 3 && !is_colorimetric = type { is_colorimetric = Image_type.colour_spaces.present 1 type; type = im_header_int "Type" im; coding = im_header_int "Coding" im; bands = im_header_int "Bands" im; } } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; ================================================ FILE: share/nip2/compat/7.8/_errors.def ================================================ /* Lots of error messages. */ errors = class { not1band = "not 1 band image"; not1band8bit = "not 1 band 8-bit image"; not1band3band = "not 1 band image, or 3 band 1 column image"; notodd = "not odd width|height"; notmask = "not mask"; notmaskim = "not image|mask, mask"; badcoding = "not NOCODING or LABPACK"; badlab = "unable to convert to LAB"; allreal = "not all real numbers"; allreg = "not all regions"; badargs = "bad arguments to "; badbands = "images have differing numbers of bands"; badcolour = "bad arg to colourspace function"; badmatrixmatch = "matrix arguments do not match"; badnum = "wrong number of real number arguments"; bandFmt = "bad BandFmt"; internal = "menu macro sanity failure!"; lengthdiff = "list arguments differ in length"; noimage = "no image argument"; noregion = "no region argument"; notim = "not image"; notimcmplx = "not image|complex"; notimnotreal = "non image arguments not all real numbers"; notimreg = "not image|region"; notregimreg = "not region, image|region"; notimregnum = "not image|region|number"; notimregstr = "not image|region|string"; notmatnum = "not real|matrix|mask"; notmatrix = "not matrix|mask"; notodd_square_matrix = "not odd-sided square matrix|mask"; notreal = "not real number"; notreglist = "not region|[region]"; notsquare_matrix = "not square matrix|mask"; regwrong = "regions not on two images"; sfacgt1 = "shrink factors should be >= 1"; unknownType = "unknown type"; usage = "usage: "; }; ================================================ FILE: share/nip2/compat/7.8/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = (unsigned int) (h ++ v) { h = (x - 1) * im_fgrey x y; v = (y - 1) * im_rot90 (im_fgrey y x); } /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12 */ image_new w h b fmt coding type pixel xoff yoff = im'''' { im = im_black w h b + pixel; im' = clip2fmt fmt im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_origin (size / 2) (size / 2) (image_set_type Image_type.LAB im) { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey size size; /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = L ++ A2 ++ A4; } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (map (const (max_value.lookup 1 0 format)) [1 .. bands]) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } ================================================ FILE: share/nip2/compat/7.8/_list.def ================================================ /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn (tl l), fn (hd l) = l; /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l up from the left using function fn and start value st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold up list l, right to left, with function fn and start * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); /* Search a list for an element, returning it's index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* land l: and all the elements of list l together * * land (map (==0) list) == true, if every element of list is zero. * land :: [bool] -> bool */ land = foldr logical_and true; /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a = l?0; b = l?1; x = tl (tl l); } /* lor l: or all the elements of list l together * * lor (map (equal 0) list) == true, if any element of list is zero. * lor :: [bool] -> bool */ lor = foldr logical_or false; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map fn' (zip2 l1 l2) { fn' p = fn p?0 p?1; } /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map fn' (zip3 l1 l2 l3) { fn' p = fn p?0 p?1 p?2; } /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = lor (map (equal x) l); /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a = hd l; x = tl l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st (hd l)) (tl l); } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a = hd l1; x = tl l1; b = hd l2; y = tl l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by fn * * split is_space "hello world" == ["hello", "world"] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.8/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true of character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && land (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, land (map is_obj l) = true, land (map is_list l) && land (map (not @ is_obj) l) && land (map is_rectangular l) && len l > 0 && land (map (equal (hd lengths)) (tl lengths)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; lengths = map len l; } /* is_matrix l: is l a list of lists of real numbers, all the same length */ is_matrix l = is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [] = is_matrix l && len l == len (hd l); /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [] = is_matrix l && (len l) % 2 == 1 && (len (l?0)) % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && (len l) % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_Guide x = is_instanceof "HGuide" x || is_instanceof "VGuide" x; is_Point x = is_instanceof "Point" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && land (map xy l) { xy l = is_real_list l && len l == 2; } ================================================ FILE: share/nip2/compat/7.8/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; join a b = a ++ b; subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; if_then_else a b c = if a then b else c; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error "unimplemented vector operation" { zeros = map (const 0) [1 .. len vec]; ones = map (const 1) [1 .. len vec]; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } /* Macbeth chart patch names. */ _macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (errors.badargs ++ "bandsplit") { bands = im_header_int "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = Image (concat (map get_value l)), is_listof (is_instanceof "Image") l = concat l, is_listof is_image l = error (errors.badargs ++ "bandjoin") { get_value x = x.value; } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = error (errors.badargs ++ "mean") { mean_op = Operator "mean" mean_object Operator_type.COMPOUND false; mean_object x = im_avg x, is_image x = mean_list x, is_real_list x || is_matrix x = error (errors.badargs ++ "mean"); mean_list l = s / n { totals = sum l; n = totals?0; s = totals?1; } // return [n, sum] for a list of numbers, or a list of list of num // etc. sum x = foldr accumulate [0, 0] x { accumulate x sofar = [n + 1, x + s], is_real x = [n + n', s + s'], is_list x = error "mean_list: not real or [real]" { n = sofar?0; s = sofar?1; sub_acc = sum x; n' = sub_acc?0; s' = sub_acc?1; } } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = error (errors.badargs ++ "deviation") { deviation_op = Operator "deviation" deviation_object Operator_type.COMPOUND false; deviation_object x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (errors.badargs ++ "deviation"); deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { totals = sum_sum2_list l; n = totals?0; s = totals?1; s2 = totals?2; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { n = sofar?0; s = sofar?1; s2 = sofar?2; sub_acc = sum_sum2_list x; n' = sub_acc?0; s' = sub_acc?1; s2' = sub_acc?2; } } } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = error (errors.badargs ++ "abs") { abs_op = Operator "abs" abs_object Operator_type.COMPOUND false; abs_object x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (errors.badargs ++ "abs"); abs_list l = (foldr1 add (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = error (errors.badargs ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec_object Operator_type.COMPOUND false; abs_vec_object x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (Vector (map abs_vec_list x)), is_matrix x = error (errors.badargs ++ "abs_vec"); abs_vec_list l = (foldr1 add (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (foldr1 add (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_matrix x, is_list x && is_list (hd x) = error (errors.badargs ++ "transpose") { transpose_op = Operator "transpose" transpose_object Operator_type.COMPOUND_REWRAP false; transpose_object x = transpose_matrix x, is_matrix x = transpose_image x, is_image x = error (errors.badargs ++ "transpose"); transpose_matrix l = [], l' == [] = (map hd l') : (transpose_matrix (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (errors.badargs ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (errors.badargs ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } rot90 x = oo_unary_function rot90_op x, is_class x = im_rot90 x, is_image x = error (errors.badargs ++ "rot90") { rot90_op = Operator "rot90" rot90_object Operator_type.COMPOUND_REWRAP false; rot90_object x = rot90_matrix x, is_matrix x = im_rot90 x, is_image x = error (errors.badargs ++ "rot90"); // slow, but what the heck rot90_matrix l = (im_rotate_dmask90 (Matrix l)).value; } rot180 x = oo_unary_function rot180_op x, is_class x = im_rot180 x, is_image x = error (errors.badargs ++ "rot180") { rot180_op = Operator "rot180" rot180_object Operator_type.COMPOUND_REWRAP false; rot180_object x = rot180_matrix x, is_matrix x = im_rot180 x, is_image x = error (errors.badargs ++ "rot180"); // slow, but what the heck rot180_matrix l = (im_rotate_dmask90 (im_rotate_dmask90 (Matrix l))).value; } rot270 x = oo_unary_function rot270_op x, is_class x = im_rot270 x, is_image x = error (errors.badargs ++ "rot270") { rot270_op = Operator "rot270" rot270_object Operator_type.COMPOUND_REWRAP false; rot270_object x = rot270_matrix x, is_matrix x = im_rot270 x, is_image x = error (errors.badargs ++ "rot270"); // slow, but what the heck rot270_matrix l = (im_rotate_dmask90 (im_rotate_dmask90 (im_rotate_dmask90 (Matrix l)))).value; } rotquad x = oo_unary_function rotquad_op x, is_class x = im_rotquad x, is_image x = error (errors.badargs ++ "rotquad") { rotquad_op = Operator "rotquad" rotquad_object Operator_type.COMPOUND_REWRAP false; rotquad_object x = rotquad_matrix x, is_matrix x = im_rotquad x, is_image x = error (errors.badargs ++ "rotquad"); rotquad_matrix l = error "rotquad matrix: not implemented"; } flipud x = oo_unary_function flipud_op x, is_class x = im_flipver x, is_image x = error (errors.badargs ++ "flipud") { flipud_op = Operator "flipud" flipud_object Operator_type.COMPOUND_REWRAP false; flipud_object x = flipud_matrix x, is_matrix x = im_flipver x, is_image x = error (errors.badargs ++ "flipud"); flipud_matrix l = reverse l; } fliplr x = oo_unary_function fliplr_op x, is_class x = im_fliphor x, is_image x = error (errors.badargs ++ "fliplr") { fliplr_op = Operator "fliplr" fliplr_object Operator_type.COMPOUND_REWRAP false; fliplr_object x = fliplr_matrix x, is_matrix x = im_fliphor x, is_image x = error (errors.badargs ++ "fliplr"); fliplr_matrix l = map reverse l; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = x, is_number x = error (errors.badargs ++ "max") { max_op = Operator "max" max_list Operator_type.COMPOUND false; max_list x = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_matrix x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = x, is_number x = error (errors.badargs ++ "min") { min_op = Operator "min" min_list Operator_type.COMPOUND false; min_list x = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_matrix x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = error (errors.badargs ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos_object Operator_type.COMPOUND false; maxpos_object x = maxpos_matrix x, is_matrix x = im_maxpos x, is_image x = error (errors.badargs ++ "maxpos"); maxpos_matrix m = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = error (errors.badargs ++ "minpos") { minpos_op = Operator "minpos" minpos_object Operator_type.COMPOUND false; minpos_object x = minpos_matrix x, is_matrix x = im_minpos x, is_image x = error (errors.badargs ++ "minpos"); minpos_matrix m = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = error (errors.badargs ++ "stats") { stats_op = Operator "stats" stats_object Operator_type.COMPOUND false; stats_object x = im_stats (to_image x).value, is_matrix x = im_stats x, is_image x = error (errors.badargs ++ "stats"); } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (errors.badargs ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = rint_value x, is_image x || is_number x = error (errors.badargs ++ "rint") { rint_op = Operator "rint" rint_value Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = scale_prim x { scale_op = Operator "scale" scale_prim Operator_type.COMPOUND_REWRAP false; scale_prim x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (errors.badargs ++ "scale"); scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (errors.badargs ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (errors.badargs ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (errors.badargs ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = im_falsecolour x, is_image x = error (errors.badargs ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (errors.badargs ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (errors.badargs ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } recomb matrix image = colour_unary recomb_op image { recomb_op x = im_recomb x matrix, is_image x = error (errors.badargs ++ "recomb"); } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = extract_area_prim obj { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" extract_area_prim Operator_type.COMPOUND_REWRAP false; extract_area_prim obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (errors.badargs ++ "extract_area"); extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_row_prim obj { y' = to_real y; extract_row_op = Operator "extract_row" extract_row_prim Operator_type.COMPOUND_REWRAP false; extract_row_prim obj = im_extract_area obj 0 y' width 1, is_image obj = [obj?y'], is_matrix obj = error (errors.badargs ++ "extract_row") { width = im_header_int "Xsize" obj; } } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_column_prim obj { x' = to_real x; extract_column_op = Operator "extract_column" extract_column_prim Operator_type.COMPOUND_REWRAP false; extract_column_prim obj = im_extract_area obj x' 0 1 height, is_image obj = map (converse cons [] @ converse subscript x') obj, is_matrix obj = error (errors.badargs ++ "extract_column") { height = im_header_int "Ysize" obj; } } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_lr_prim a b { join_lr_op = Operator "join_lr" join_lr_prim Operator_type.COMPOUND_REWRAP false; join_lr_prim a b = im_extract_area (im_insert a b a_width 0) 0 0 (a_width + b_width) out_height, is_image a && is_image b = map2 join a b, is_matrix a && is_matrix b = error (errors.badargs ++ "join_lr") { a_height = im_header_int "Ysize" a; b_height = im_header_int "Ysize" b; a_width = im_header_int "Xsize" a; b_width = im_header_int "Xsize" b; out_height = min_pair a_height b_height; } } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_tb_prim a b { join_tb_op = Operator "join_tb" join_tb_prim Operator_type.COMPOUND_REWRAP false; join_tb_prim a b = im_extract_area (im_insert a b 0 a_height) 0 0 out_width (a_height + b_height), is_image a && is_image b = map (take out_matrix_width) a ++ map (take out_matrix_width) b, is_matrix a && is_matrix b = error (errors.badargs ++ "join_tb") { a_height = im_header_int "Ysize" a; b_height = im_header_int "Ysize" b; a_width = im_header_int "Xsize" a; b_width = im_header_int "Xsize" b; out_width = min_pair a_width b_width; out_matrix_width = min_pair a_matrix_width b_matrix_width { a_matrix_width = len a?0; b_matrix_width = len b?0; } } } insert x y small big = oo_binary_function insert_op small big, is_class small = oo_binary'_function insert_op small big, is_class big = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (errors.badargs ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (errors.badargs ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } rotate angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_similarity image (cos angle) (sin angle) 0 0, is_real angle && is_image image = error (errors.badargs ++ "rotate") { rotate_op = Operator "rotate" rotate Operator_type.COMPOUND_REWRAP false; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = im_header_int "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function (clip2fmt_op format) image, is_class image = im_clip2fmt image format, is_image image = error (errors.badargs ++ "clip2fmt") { clip2fmt_op format = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { image = (unsigned char) im_mask2vips (Matrix m2); m2_width = im_header_int "Xsize" image; m2_height = im_header_int "Ysize" image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = im_embed image 0 (m1.width - 1) (m1.height - 1) (m2_width + 2 * (m1.width - 1)) (m2_height + 2 * (m1.height - 1)); // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image mask, is_image image = error (errors.badargs ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image mask, is_image image = error (errors.badargs ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image mask, is_image image = error (errors.badargs ++ "conv") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image w h n, is_image image = error (errors.badargs ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } hist_find image = oo_unary_function hist_find_op image, is_class image = im_histgr image (-1), is_image image = error (errors.badargs ++ "hist_find") { hist_find_op = Operator "hist_find" hist_find Operator_type.COMPOUND_REWRAP false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image bins, is_image image = error (errors.badargs ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" hist_find_nD Operator_type.COMPOUND_REWRAP false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (errors.badargs ++ "hist_map") { hist_map_op = Operator "hist_map" hist_map Operator_type.COMPOUND_REWRAP false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (errors.badargs ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (errors.badargs ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (errors.badargs ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = im_lhisteq image w h, is_image image = error (errors.badargs ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; } resize xfac yfac interp image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (errors.badargs ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac yfac, is_int xfac && is_int yfac && xfac >= 1 && yfac >= 1 && interp == Interpolate.nearest_neighbour // downscale by integer factor, nearest neighbour = im_subsample im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && interp == Interpolate.nearest_neighbour // upscale by any factor, nearest neighbour // can't really do this right ... upscale by integer part, then // bilinear to exact size = scale (break xfac)?1 (break yfac)?1 (im_zoom im (break xfac)?0 (break yfac)?0), xfac >= 1 && yfac >= 1 && interp == Interpolate.nearest_neighbour // downscale by any factor, nearest neighbour // can't really do this right ... downscale by integer part, // then bilinear to exact size = scale (1 / (break xfac')?1) (1 / (break yfac')?1) (im_subsample im (break xfac')?0 (break yfac')?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.nearest_neighbour // upscale by any factor, bilinear = scale xfac yfac im, xfac >= 1 && yfac >= 1 && interp == Interpolate.bilinear // downscale by any factor, bilinear // block shrink by integer factor, then bilinear resample to // exact = scale (1 / (break xfac')?1) (1 / (break yfac')?1) (im_shrink im (break xfac')?0 (break yfac')?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.bilinear = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac ++ "\n" ++ " yfac = " ++ print yfac ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate.names.lookup 1 0 interp ++ ")") { xfac' = 1 / xfac; yfac' = 1 / yfac; // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // binlinear resize scale xfac yfac im = im_affine im xfac 0 0 yfac 0 0 0 0 (width * xfac) (height * yfac) { width = im_header_int "Xsize" im; height = im_header_int "Ysize" im; } } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map3 (map_trinary fn) a b c, is_list a && is_list b && is_list c = map2 (map_trinary fn a) b c, is_list b && is_list c = map2 (map_trinary (converse31 fn) b) a c, is_list a && is_list c = map2 (map_trinary (converse32 fn) c) a b, is_list a && is_list b = map (map_trinary fn a b) c, is_list c = map (map_trinary (converse32 fn) a c) b, is_list b = map (map_trinary (converse34 fn) b c) a, is_list a = fn a b c { converse31 fn a b c = fn b a c; converse32 fn a b c = fn c a b; converse33 fn a b c = fn a c b; converse34 fn a b c = fn b c a; } /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map2 (map_binary fn) a b, is_list a && is_list b = map (map_binary fn a) b, is_list b = map (map_binary (converse fn) b) a, is_list a = fn a b; /* Map a 1-ary function over an object. */ map_unary fn a = map (map_unary fn) a, is_list a = fn a; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop block_size overlap i = map chop' [0, step .. height] { width = im_header_int "Xsize" i; height = im_header_int "Ysize" i; bands = im_header_int "Bands" i; format = im_header_int "BandFmt" i; type = im_header_int "Type" i; /* Unique pixels per tile. */ step = block_size - overlap; /* Calculate padding ... pad up to block_size pixel boundary. */ sx = block_size + (width - width % step); sy = block_size + (height - height % step); /* Expand image with black to pad size. */ background = image_new sx sy bands format Image_coding.NOCODING type 0 0 0; pad = im_insert background i 0 0; /* Chop up a row. */ chop' y = map chop'' [0, step .. width] { chop'' x = im_extract_area pad x y block_size block_size; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = vjoin (map hjoin il) { /* Join a list of tiles horizontally. */ hjoin l = foldl1 lrj l { lrj l r = im_lrmerge l r (hoverlap - im_header_int "Xsize" l) 0 10; } /* Join a list of tiles vertically. */ vjoin l = foldl1 tbj l { tbj t b = im_tbmerge t b 0 (voverlap - im_header_int "Ysize" t) 10; } } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } ================================================ FILE: share/nip2/compat/7.8/_types.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), "any"]; check_bool = [is_bool, "boolean"]; check_real = [is_real, "real"]; check_ureal = [is_ureal, "unsigned real"]; check_preal = [is_preal, "positive real"]; check_real_list = [is_real_list, "list of real"]; check_string = [is_string, "string"]; check_string_list = [is_string_list, "list of string"]; check_int = [is_int, "integer"]; check_uint = [is_uint, "unsigned integer"]; check_pint = [is_pint, "positive integer"]; check_matrix = [is_matrix, "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, "0, 1, 2 or 3"]; check_image = [is_image, "image"]; check_xy_list = [is_xy_list, "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_instanceof "Matrix_base", "Matrix"]; check_colour_space = [is_colour_space, "colour_space"]; check_rectangular = [is_rectangular, "rectangular [[*]]"]; check_Guide = [is_Guide, "HGuide or VGuide"]; check_Point = check_instance "Point"; check_Colour = check_instance "Colour"; /* Check a set of args. Grab _check_table. It's a list of two check * lists: the first checks each arg, and the second checks all args * together. * * - each line in argcheck is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in allcheck is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down a bit. */ check_args x = x, badargs == [] && badalls == [] = error message { argcheck = x._check_args; allcheck = x._check_all; // join two strings up with a separator string join_sep j a b = a ++ j ++ b; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = errors.badargs ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\nusage\n" ++ indent ++ usage ++ "\nwhere\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ "you passed " ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ "you passed\n" ++ concat (map fmt_arg argcheck) { fmt n = "condition failed: " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make usage note usage = x.name ++ " " ++ foldr (join_sep " ") [] (map (extract 1) argcheck); // make arg type notes arg_types = foldr (join_sep "\n") [] (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "and\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = foldr (join_sep "\n") [] all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error ("unknown binary operator: " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error ("unknown unary operator: " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = error ("No method found for binary operator.\n" ++ "left = " ++ print a ++ "\n" ++ "operator = " ++ op.op_name ++ "\n" ++ "right = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = error ("No method found for binary operator.\n" ++ "left = " ++ print a ++ "\n" ++ "operator = " ++ op.op_name ++ "\n" ++ "right = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error ("No method found for unary operator.\n" ++ "operator = " ++ op.op_name ++ "\n" ++ "argument = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; /* Default: no checks ... override in subclasses. */ _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; /* Provide a fallback for class == thing ... just use pointer * equality. */ oo_binary_table op x = [ [ pointer_equal this x, op.op_name == "equal" || op.op_name == "equal'" ], [ not_pointer_equal this x, op.op_name == "not_equal" || op.op_name == "not_equal'" ] ]; oo_unary_table op = []; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ] ++ super._check_args; // methods oo_binary_table op x = [ [ this.Real (op.fn this.value x.value), is_instanceof "Real" x && op.type == Operator_type.ARITHMETIC ], [ this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ], [ op.fn this.value x.value, is_instanceof "Real" x && op.type == Operator_type.RELATIONAL ], [ op.fn this.value x, !is_class x ] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [ this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC ], [ op.fn this.value, true ] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ] ++ super._check_args; // methods oo_binary_table op x = [ [ if value then x?0 else x?1, op.op_name == "if_then_else" ], [ this.Bool (op.fn this.value x.value), is_instanceof "Bool" x ], [ this.Bool (op.fn this.value x), is_bool x ] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [ this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL ] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ] ++ super._check_args; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ] ++ super._check_args; Real value = Number caption value; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ] ++ super._check_args; } // the old name Filename = Pathname "Pick a file"; /* Vector type ... just a finite list of real ... handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ] ++ super._check_args; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [ this.Vector (op.fn this.value x.value), is_instanceof "Vector" x && (op.op_name == "join" || op.op_name == "join'") ], // extra check for lengths equal [ this.Vector (map_binary op.fn this.value x.value), is_instanceof "Vector" x && len value == len x.value && op.type == Operator_type.ARITHMETIC ], [ this.Vector (map_binary op.fn this.value x.value), is_instanceof "Real" x && op.type == Operator_type.ARITHMETIC ], [ this.Vector (map_binary op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ], // need extra length check [ this.Vector (map bool_to_real (map_binary op.fn this.value x.value)), is_instanceof "Vector" x && len value == len x.value && op.type == Operator_type.RELATIONAL ], [ this.Vector (map bool_to_real (map_binary op.fn this.value x.value)), is_instanceof "Real" x && op.type == Operator_type.RELATIONAL ], [ this.Vector (map bool_to_real (map_binary op.fn this.value x)), is_real x && op.type == Operator_type.RELATIONAL ], [ this.Vector (op.fn this.value x.value), is_instanceof "Vector" x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP ], [ x.Image (vec op'.op_name x.value value), is_instanceof "Image" x ], [ vec op'.op_name x value, is_image x ], [ op.fn this.value x, is_real x ] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [ this.Vector (map_unary op.fn this.value), op.type == Operator_type.ARITHMETIC ], [ this.Vector (map bool_to_real (map_unary op.fn this.value)), op.type == Operator_type.RELATIONAL ], [ this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP ], [ op.fn this.value, true ] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ] ++ super._check_args; // calculate these from value width = len value?0; height = len value; // methods oo_binary_table op x = [ // mat multiply is special [ this.Matrix_base mul.value, is_instanceof "Matrix_base" x && op.op_name == "multiply" ], [ this.Matrix_base mul'.value, is_instanceof "Matrix_base" x && op.op_name == "multiply'" ], // mat divide is also special [ this.Matrix_base div.value, is_instanceof "Matrix_base" x && op.op_name == "divide" ], [ this.Matrix_base div'.value, is_instanceof "Matrix_base" x && op.op_name == "divide'" ], // power -1 means invert [ this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power" ], [ this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power" ], [ error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'" ], // matrix op vector ... treat a vector as a 1 row matrix [ this.Matrix_base (map (map_binary op'.fn x.value) this.value), is_instanceof "Vector" x && op.type == Operator_type.ARITHMETIC ], [ this.Matrix_base (map_binary op.fn this.value x.value), (is_instanceof "Matrix_base" x || is_instanceof "Real" x) && op.type == Operator_type.ARITHMETIC ], [ this.Matrix_base (map_binary op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ], // compound ... don't do iteration [ this.Matrix_base (op.fn this.value x.value), (is_instanceof "Matrix_base" x || is_instanceof "Real" x || is_instanceof "Vector" x) && op.type == Operator_type.COMPOUND_REWRAP ] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [ this.Matrix_base (map_unary op.fn this.value), op.type == Operator_type.ARITHMETIC ], [ this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP ], [ op.fn this.value, true ] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ] ++ super._check_args; Matrix_base value = Matrix_vips value scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {}; /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = im_read_dmask (expand filename); /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ] ++ super._check_args; _check_all = [ [len value == 3, "len value == 3"] ] ++ super._check_all; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = im_header_int "Type" im; bands = im_header_int "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [ itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP ] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [ itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP ] ] ++ super.oo_unary_table op; Vector value = Colour colour_space value; } /* Base slider type. */ Scale caption from to value = class scope.Real value { _check_args = [ [from, "from", check_real], [to, "to", check_real] ] ++ super._check_args; _check_all = [ [from < to, "from < to"] ] ++ super._check_all; // methods oo_binary_table op x = [ [ this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_instanceof "Scale" x && op.type == Operator_type.ARITHMETIC ], [ this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ] ] ++ super.oo_binary_table op x; Real value = Scale caption from to value; } Slider = Scale ""; /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ] ++ super._check_args; Bool value = Toggle caption value; } /* Base option type. */ Option caption labels value = class Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ] ++ super._check_args; } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ] ++ super._check_args; /* present col x: is there an x in column col */ present col x = member (map (extract col) value) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error ("item " ++ print x ++ " not in table") { n = index (equal x) (map (extract from) value); } } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ] ++ super._check_args; // derived right = left + width; bottom = top + height; // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // equal to another rect equal r = left == r.left && top == r.top && width == r.width && height == r.height; // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); // operator overloading // just define equal and not equal oo_binary_table op x = [ [ equal x, is_instanceof "Rect" x && (op.op_name == "equal" || op.op_name == "equal'") ], [ !equal x, is_instanceof "Rect" x && (op.op_name == "not_equal" || op.op_name == "not_equal'") ] ] ++ super.oo_binary_table op x; } /* Values for Compression field in image. */ Image_compression = class { NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NOCODING = 0; COLQUANT = 1; LABPACK = 2; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647 // INT ] ? fmt, fmt >= 0 && fmt <= INT = error errors.bandFmt; } /* Type field. */ Image_type = class { FOURIER = 24; YXY = 23; sRGB = 22; LABS = 21; LCH = 19; UCS = 18; RGB = 17; LABQ = 16; CMYK = 15; CMC = 14; LAB = 13; XYZ = 12; LUT = 11; HISTOGRAM = 10; POWER_SPECTRUM = 9; BLUE_ONLY = 8; GREEN_ONLY = 7; RED_ONLY = 6; YUV = 5; IR = 4; XRAY = 3; LUMINACE = 2; B_W = 1; MULTIBAND = 0; /* Table to get names <-> numbers. */ type_names = Table [ [ "FOURIER", FOURIER ], [ "YXY", YXY ], [ "sRGB", sRGB ], [ "LABS", LABS ], [ "LCH", LCH ], [ "UCS", UCS ], [ "RGB", RGB ], [ "LABQ", LABQ ], [ "CMYK", CMYK ], [ "CMC", CMC ], [ "LAB", LAB ], [ "XYZ", XYZ ], [ "LUT", LUT ], [ "HISTOGRAM", HISTOGRAM ], [ "POWER_SPECTRUM", POWER_SPECTRUM ], [ "BLUE_ONLY", BLUE_ONLY ], [ "GREEN_ONLY", GREEN_ONLY ], [ "RED_ONLY", RED_ONLY ], [ "YUV", YUV ], [ "IR", IR ], [ "XRAY", XRAY ], [ "LUMINACE", LUMINACE ], [ "B_W", B_W ], [ "MULTIBAND", MULTIBAND ] ]; /* Table relating nip's colour space names and VIPS's Type numbers. */ colour_spaces = Table [ [ "XYZ", Image_type.XYZ ], [ "Yxy", Image_type.YXY ], [ "Lab", Image_type.LAB ], [ "LCh", Image_type.LCH ], [ "UCS", Image_type.UCS ], [ "RGB", Image_type.RGB ], [ "sRGB", Image_type.sRGB ] ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ] ++ super._check_args; // fields from VIPS header width = im_header_int "Xsize" value; height = im_header_int "Ysize" value; bands = im_header_int "Bands" value; format = im_header_int "BandFmt" value; coding = im_header_int "Coding" value; type = im_header_int "Type" value; xres = im_header_double "Xres" value; yres = im_header_double "Yres" value; xoffset = im_header_int "Xoffset" value; yoffset = im_header_int "Yoffset" value; filename = im_header_string "filename" value; // convenience ... the area our pixels occupy as a rect rect = Rect (-xoffset) (-yoffset) width height; // extract an area, addressed in nip cordinates extract_area left top width height = im_extract_area value (left + xoffset) (top + yoffset) width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ [ this.Image (op.fn this.value x.value), is_instanceof "Image" x || is_instanceof "Real" x ], [ this.Image result_image, op.op_name == "if_then_else" ], [ this.Image (vec op.op_name value x.value), is_instanceof "Vector" x ], [ this.Image (op.fn this.value x), is_number x || is_image x ] ] ++ super.oo_binary_table op x { to_image x = x, is_image x = x.value, is_instanceof "Image" x = black + x { black = im_black width height target_bands; } // get a member from the first of a list of objects to have it get_property has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } // get things about our output from inputs in this order objects = [then_part, else_part, this]; then_part = x?0; else_part = x?1; // properties of our output image target_bands = get_property (has_member "bands") (get_member "bands") objects; target_format = get_property (has_member "format") (get_member "format") objects; target_type = get_property has_type get_type objects; then_image = to_image then_part; else_image = to_image else_part; then_image' = clip2fmt target_format then_image; else_image' = clip2fmt target_format else_image; result_image = image_set_type target_type (if value then then_image' else else_image'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [ this.Image result, is_image result ], [ result, true ] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file str = class Image value { _check_args = [ [str, "str", check_string] ] ++ super._check_args; file = Filename str; value = vips_image file.value; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ] ++ super._check_args; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = image.extract_area left top width height, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = Area image left top width height; } Arrow image left top width height = class Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ] ++ super._check_args; _check_all = [ [image.rect.includes_rect this, "image.rect.includes_rect this"] ] ++ super._check_all; // our rect, translated to image cods (ie. offset by origin) image_rect = Rect (left + image.xoffset) (top + image.yoffset) width height; // methods oo_binary_table op x = [ [ this.Arrow this.image left' top' width' height', is_instanceof "Arrow" x && op.type == Operator_type.ARITHMETIC ] ] ++ super.oo_binary_table op x { left' = op.fn this.left x.left; top' = op.fn this.top x.top; width' = op.fn this.width x.width; height' = op.fn this.height x.height; } oo_unary_search op = [ [ this.Arrow this.image left' top' width' height', op.type == Operator_type.ARITHMETIC ] ] ++ super.oo_unary_table op { left' = op.fn this.left; top' = op.fn this.top; width' = op.fn this.width; height' = op.fn this.height; } } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = VGuide image left; } Point image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = Point image left top; } // Mark is the name nip2 expects for Point Mark = Point; // convenience functions: make relative on an image ... subtract origin // offset, ie. make in pixel coordinates Region_relative image u v w h = Region image (image.width * u - image.xoffset) (image.height * v - image.yoffset) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u - image.xoffset) (image.height * v - image.yoffset) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u - image.xoffset) (image.height * v - image.yoffset) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v - image.yoffset); HGuide_relative image u = HGuide image (image.width * u - image.xoffset); Point_relative image u v = Point image (image.width * u - image.xoffset) (image.height * v - image.yoffset); Interpolate = class { nearest_neighbour = 0; bilinear = 1; bicubic = 2; /* Table to map interpol numbers to descriptive strings */ names = Table [ [ "Nearest neighbour", nearest_neighbour ], [ "Bilinear", bilinear ], [ "Bicubic", bicubic ] ]; } Separator = class {} // renamed in 7.9 estpar = im_estpar; resample = im_transform_search; ================================================ FILE: share/nip2/compat/7.9/Capture.def ================================================ /* make a new capture image */ Capture_video = class Image value { // shortcut to prefs _prefs = Workspaces.Preferences; device = _prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] _prefs.VIDEO_CHANNEL; brightness = Slider 0 32767 _prefs.VIDEO_BRIGHTNESS; colour = Slider 0 32767 _prefs.VIDEO_COLOUR; contrast = Slider 0 32767 _prefs.VIDEO_CONTRAST; hue = Slider 0 32767 _prefs.VIDEO_HUE; frames_hint = "Average this many frames:"; frames = Slider 0 100 _prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" _prefs.VIDEO_MONO; crop = Toggle "Crop image" _prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value brightness.value colour.value contrast.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = _prefs.VIDEO_CROP_LEFT; top = _prefs.VIDEO_CROP_TOP; width = min_pair _prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair _prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_hint = "Stretch vertically by this factor:"; aspect_ratio = _prefs.VIDEO_ASPECT; value = frame' { frame = edit_crop.value, crop = _raw_grab.value; frame' = colour_transform Image_type.sRGB Image_type.B_W frame, mono = frame; } } #separator /* use white image w to correct image i */ Light_correct w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } /* equalize bands in region r */ White_balance r = map_unary wb r { wb region = clip2fmt region.format (region.image * Vector facs) { target = mean region; facs = map (divide target) (map mean (bandsplit region)); } } /* remove features larger than a certain size from image x */ Smooth_image x = map_unary smooth x { smooth image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; feature = Slider 1 50 20; value = im_resize_linear (im_shrink image.value feature.value feature.value) image.width image.height; } } #separator /* calculate RGB -> LAB transform from an image of a Macbeth colour chart */ Calibrate_chart image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; // get macbeth data file to use macbeth = Filename "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = im_measure image.value 0 0 image.width image.height 6 4; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'); // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it linearising_lut = Image _linear_lut; // map the original image through the lineariser to get linear 0-1 // RGB image _image' = im_maplut image.value linearising_lut.value; // remeasure and solve for RGB -> XYZ _camera' = im_measure _image' 0 0 image.width image.height 6 4; _pinv = (transpose _camera' * _camera') ** -1; M = transpose (_pinv * transpose _camera' * _true_XYZ); // convert linear RGB camera to Lab value = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb M _image')); // measure again and compute dE76 _camera'' = im_measure value 0 0 image.width image.height 6 4; _dEs = map abs_vec (map Vector (_camera'' - _true_Lab).value); final_dE76 = mean (Vector _dEs); _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = _macbeth_names ? _worst ++ " (patch " ++ print (_worst + 1) ++ ")"; } /* apply RGB -> LAB transform to an image */ Calibrate_image calib image = class Image value { _check_args = [ [calib, "calib", check_instance "Calibrate_chart"], [image, "image", check_Image] ] ++ super._check_args; // map the original image through the lineariser to get linear 0-1 // RGB image _image' = im_maplut image.value calib.linearising_lut.value; // convert linear RGB camera to Lab value = colour_transform Image_type.XYZ Image_type.LAB ((float) (recomb calib.M _image')); } ================================================ FILE: share/nip2/compat/7.9/Colour.def ================================================ /* Save a bit of typing. */ _colour_conv from to x = map_unary (colour_transform from to) x; /* convert Mono to various formats */ Mono_to = class { /* convert mono colourspace to mono colourspace */ Mono x = _colour_conv Image_type.B_W Image_type.B_W x; /* convert mono colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.B_W Image_type.XYZ x; /* convert mono colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.B_W Image_type.YXY x; /* convert mono colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.B_W Image_type.LAB x; /* convert mono colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.B_W Image_type.LCH x; /* convert mono colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.B_W Image_type.UCS x; /* convert mono colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.B_W Image_type.RGB x; /* convert mono colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.B_W Image_type.sRGB x; /* convert mono colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.B_W Image_type.LABQ x; /* convert mono colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.B_W Image_type.LABS x; } /* convert XYZ to various formats */ XYZ_to = class { /* convert XYZ colourspace to mono colourspace */ Mono x = _colour_conv Image_type.XYZ Image_type.B_W x; /* convert XYZ colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.XYZ Image_type.XYZ x; /* convert XYZ colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.XYZ Image_type.YXY x; /* convert XYZ colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.XYZ Image_type.LAB x; /* convert XYZ colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.XYZ Image_type.LCH x; /* convert XYZ colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.XYZ Image_type.UCS x; /* convert XYZ colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.XYZ Image_type.RGB x; /* convert XYZ colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.XYZ Image_type.sRGB x; /* convert XYZ colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.XYZ Image_type.LABQ x; /* convert XYZ colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.XYZ Image_type.LABS x; } /* convert Yxy to various formats */ Yxy_to = class { /* convert Yxy colourspace to mono colourspace */ Mono x = _colour_conv Image_type.YXY Image_type.B_W x; /* convert Yxy colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.YXY Image_type.XYZ x; /* convert Yxy colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.YXY Image_type.YXY x; /* convert Yxy colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.YXY Image_type.LAB x; /* convert Yxy colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.YXY Image_type.LCH x; /* convert Yxy colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.YXY Image_type.UCS x; /* convert Yxy colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.YXY Image_type.RGB x; /* convert Yxy colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.YXY Image_type.sRGB x; /* convert Yxy colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.YXY Image_type.LABQ x; /* convert Yxy colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.YXY Image_type.LABS x; } /* convert Lab to various formats */ Lab_to = class { /* convert Lab colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LAB Image_type.B_W x; /* convert Lab colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LAB Image_type.XYZ x; /* convert Lab colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LAB Image_type.YXY x; /* convert Lab colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LAB Image_type.LAB x; /* convert Lab colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LAB Image_type.LCH x; /* convert Lab colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LAB Image_type.UCS x; /* convert Lab colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LAB Image_type.RGB x; /* convert Lab colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LAB Image_type.sRGB x; /* convert Lab colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LAB Image_type.LABQ x; /* convert Lab colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LAB Image_type.LABS x; } /* convert LCh to various formats */ LCh_to = class { /* convert LCh colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LCH Image_type.B_W x; /* convert LCh colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LCH Image_type.XYZ x; /* convert LCh colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LCH Image_type.YXY x; /* convert LCh colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LCH Image_type.LAB x; /* convert LCh colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LCH Image_type.LCH x; /* convert LCh colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LCH Image_type.UCS x; /* convert LCh colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LCH Image_type.RGB x; /* convert LCh colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LCH Image_type.sRGB x; /* convert LCh colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LCH Image_type.LABQ x; /* convert LCh colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LCH Image_type.LABS x; } /* convert UCS to various formats */ UCS_to = class { /* convert UCS colourspace to mono colourspace */ Mono x = _colour_conv Image_type.UCS Image_type.B_W x; /* convert UCS colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.UCS Image_type.XYZ x; /* convert UCS colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.UCS Image_type.YXY x; /* convert UCS colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.UCS Image_type.LAB x; /* convert UCS colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.UCS Image_type.LCH x; /* convert UCS colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.UCS Image_type.UCS x; /* convert UCS colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.UCS Image_type.RGB x; /* convert UCS colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.UCS Image_type.sRGB x; /* convert UCS colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.UCS Image_type.LABQ x; /* convert UCS colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.UCS Image_type.LABS x; } /* convert RGB to various formats */ RGB_to = class { /* convert RGB colourspace to mono colourspace */ Mono x = _colour_conv Image_type.RGB Image_type.B_W x; /* convert RGB colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.RGB Image_type.XYZ x; /* convert RGB colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.RGB Image_type.YXY x; /* convert RGB colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.RGB Image_type.LAB x; /* convert RGB colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.RGB Image_type.LCH x; /* convert RGB colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.RGB Image_type.UCS x; /* convert RGB colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.RGB Image_type.RGB x; /* convert RGB colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.RGB Image_type.sRGB x; /* convert RGB colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.RGB Image_type.LABQ x; /* convert RGB colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.RGB Image_type.LABS x; } /* convert sRGB to various formats */ sRGB_to = class { /* convert sRGB colourspace to mono colourspace */ Mono x = _colour_conv Image_type.sRGB Image_type.B_W x; /* convert sRGB colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.sRGB Image_type.XYZ x; /* convert sRGB colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.sRGB Image_type.YXY x; /* convert sRGB colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.sRGB Image_type.LAB x; /* convert sRGB colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.sRGB Image_type.LCH x; /* convert sRGB colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.sRGB Image_type.UCS x; /* convert sRGB colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.sRGB Image_type.RGB x; /* convert sRGB colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.sRGB Image_type.sRGB x; /* convert sRGB colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.sRGB Image_type.LABQ x; /* convert sRGB colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.sRGB Image_type.LABS x; } /* convert LabQ to various formats */ LabQ_to = class { /* convert LabQ colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LABQ Image_type.B_W x; /* convert LabQ colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LABQ Image_type.XYZ x; /* convert LabQ colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LABQ Image_type.YXY x; /* convert LabQ colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LABQ Image_type.LAB x; /* convert LabQ colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LABQ Image_type.LCH x; /* convert LabQ colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LABQ Image_type.UCS x; /* convert LabQ colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LABQ Image_type.RGB x; /* convert LabQ colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LABQ Image_type.sRGB x; /* convert LabQ colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LABQ Image_type.LABQ x; /* convert LabQ colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LABQ Image_type.LABS x; } /* convert LabS to various formats */ LabS_to = class { /* convert LabS colourspace to mono colourspace */ Mono x = _colour_conv Image_type.LABS Image_type.B_W x; /* convert LabS colourspace to XYZ colourspace */ XYZ x = _colour_conv Image_type.LABS Image_type.XYZ x; /* convert LabS colourspace to Yxy colourspace */ Yxy x = _colour_conv Image_type.LABS Image_type.YXY x; /* convert LabS colourspace to Lab colourspace */ Lab x = _colour_conv Image_type.LABS Image_type.LAB x; /* convert LabS colourspace to LCh colourspace */ LCh x = _colour_conv Image_type.LABS Image_type.LCH x; /* convert LabS colourspace to UCS colourspace */ UCS x = _colour_conv Image_type.LABS Image_type.UCS x; /* convert LabS colourspace to RGB colourspace */ RGB x = _colour_conv Image_type.LABS Image_type.RGB x; /* convert LabS colourspace to sRGB colourspace */ sRGB x = _colour_conv Image_type.LABS Image_type.sRGB x; /* convert LabS colourspace to LabQ colourspace */ LabQ x = _colour_conv Image_type.LABS Image_type.LABQ x; /* convert LabS colourspace to LabS colourspace */ LabS x = _colour_conv Image_type.LABS Image_type.LABS x; } #separator /* recombine image bands with an editable matrix */ Colour_recombination in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; matrix = Matrix_rec (identity_matrix image.bands); value = recomb matrix image.value; } } /* colour temperature conversions */ Colour_temperature = class { /* convert XYZ from D65 to D50 ... use the Bradford approximation */ D65XYZ_to_D50XYZ in = map_unary (colour_unary im_D652D50) in; /* convert XYZ from D50 to D65 ... use the Bradford approximation */ D50XYZ_to_D65XYZ in = map_unary (colour_unary im_D502D65) in; } /* various colour difference metrics */ dE_ = class { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ _apply_cvt cvt x = cvt x, is_instanceof "Image" x || is_instanceof "Colour" x || is_image x = x; _diff cvt in1 in2 = abs_vec (_apply_cvt cvt in1 - _apply_cvt cvt in2); /* Converter to LAB. */ _lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ _ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; /* calculate delta-E CIE76 for two objects */ CIE76 in1 in2 = map_binary (_diff _lab_cvt) in1 in2; /* calculate delta-E00 (CIEDE2000) for two objects */ CIE00 in1 in2 = map_binary (colour_binary "im_dE00_fromLab" im_dE00_fromLab) in1 in2; /* calculate delta-E CMC(1:1) for two objects */ UCS in1 in2 = map_binary (_diff _ucs_cvt) in1 in2; } #separator /* apply a coloured tint to a monochrome image */ Tint_mono_image in = map_unary apply_tint in { apply_tint in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; tint = Colour "Lab" [50, 0, 0]; value = image_set_type Image_type.LAB (fancytint inp l_tint a_tint b_tint) { // input image ... to L only inp_lab = colour_transform_to Image_type.LAB in.value; inp = inp_lab?0; // make sure tint is LAB (might be edited) lab_tint = colour_transform_to Image_type.LAB tint; // selected lab l_tint = lab_tint.value?0; a_tint = lab_tint.value?1; b_tint = lab_tint.value?2; // fancy tint function ... don't tint black and white fancytint im l a b = im ++ ima ++ imb { mod = (100 - im) / (100 - l), im > l = im / l; backgr = image_new in.width in.height 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; ima = mod * (backgr + a); imb = mod * (backgr + b); } } } } /* displace neutral axis in LAB colourspace */ Adjust_cast image = map_unary widget image { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; green_red = Slider (-20) 20 0; blue_yellow = Slider (-20) 20 0; value = (colour_transform_to (get_type image) image'').value { image' = colour_transform_to Image_type.LAB image; image'' = image' + Vector [0, green_red.value, blue_yellow.value]; } } } /* displace h, scale LC in LCh colourspace */ Adjust_hue_saturation_brightness image = map_unary widget image { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; hue = Slider 0 360 0; saturation = Slider 0.01 5 1; brightness = Slider 0.01 5 1; value = (colour_transform_to (get_type image) image'').value { image' = colour_transform_to Image_type.LCH image; image'' = image' * Vector [bv, sv, 1] + Vector [0, 0, hv]; bv = brightness.value; sv = saturation.value; hv = hue.value; } } } /* find pixels with a similar colour */ Similar_colour image = map_unary match image { match image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; target_patch = Region image (20 - image.xoffset) (20 - image.yoffset) 10 10; target_colour = Colour_from_image target_patch; dE_threshold = Slider 0 100 10; value = (dE_.CIE76 image target_colour < dE_threshold).value; } } /* plot an ab scatter histogram */ Plot_ab_scatter image = map_unary widget image { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; bins = 8; value = bg * (((90 / mx) * hist) ++ blk) { lab = colour_transform_to Image_type.LAB image.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins ab; mx = max hist; bg = lab_slice bins 1; blk = 1 + im_black bins bins 2; } } } ================================================ FILE: share/nip2/compat/7.9/Filter.def ================================================ /* Some useful masks. */ _filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; _filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; _filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; _filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; _filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; _filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; /* 3x3 blur of image */ Blur x = map_unary (conv _filter_blur) x; /* 3x3 sharpen of image */ Sharpen x = map_unary (conv _filter_sharp) x; /* 1-pixel displacement emboss */ Emboss x = map_unary (conv _filter_emboss) x; /* 3x3 median filter of image */ Median x = map_unary (rank 3 3 5) x; /* 3x3 laplacian of image */ Laplacian x = map_unary (conv _filter_laplacian) x; /* 3x3 Sobel edge detect of image */ Sobel x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv _filter_sobel im; b = conv (rot270 _filter_sobel) im; } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 _filter_lindet); images = map (converse conv im) masks; } } #separator /* custom convolution of an image */ Custom_convolution in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 off off image.width image.height, border = image' { conv_op = im_conv_raw, !separable && type == 0 = im_convsep_raw, separable && type == 0 = im_convf_raw, !separable && type == 1 = im_convsepf_raw, separable && type == 1 = error "boink!"; image' = conv_op image.value matrix; off = (matrix.width + 1) / 2; } } } /* blur with a radius and shape */ Custom_blur in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; radius = Slider 1 50 1; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; value = im_convsep in.value mask { /* Make a square mask. */ mask_sq_line = map (const 1) [1 .. 2 * radius.value - 1]; mask_sq_sum = foldr1 add mask_sq_line; mask_sq_sep = Matrix_con mask_sq_sum 0 [mask_sq_line]; /* Make a gaussian mask. sigma is not really related * to radius very well, sort-of bodge it. */ mask_g = im_gauss_imask (radius.value / 2) 0.2; mask_g_line = mask_g.value ? (mask_g.height / 2); mask_g_sum = foldr1 add mask_g_line; mask_g_sep = Matrix_con mask_g_sum 0 [mask_g_line]; mask = mask_sq_sep, shape.value == 0 = mask_g_sep; } } } /* cored sharpen of L only in LAB image */ Custom_sharpen in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; size = Option "Mask size" [ "3 x 3", "5 x 5", "7 x 7", "9 x 9", "11 x 11" ] 0; smooth_threshold = Slider 0 5 1.5; brighten_max = Slider 1 50 10; darken_max = Slider 1 50 50; flat_sharp = Slider (-2) 5 1; jaggy_sharp = Slider (-2) 5 2; value = im_sharpen in.value [3, 5, 7, 9, 11]?size smooth_threshold.value brighten_max.value darken_max.value flat_sharp.value jaggy_sharp.value; } } /* custom rank filter of an image */ Custom_rank in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; window_size_hint = "Length of side of window to pass " ++ "over image:"; window_size = 3; rank = (int) ((window_size * window_size + 1) / 2); border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 off off image.width image.height, border = image' { image' = im_rank_raw image.value window_size window_size rank; off = (window_size + 1) / 2; } } } /* statistical difference of an image */ Statistical_difference in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; window_size_hint = "Length of side of window to pass " ++ "over image:"; window_size = 11; target_mean = 128; target_mean_weight = Slider 0 1 0.8; target_deviation = 50; target_deviation_weight = Slider 0 1 0.8; border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 off off image.width image.height, border = image' { image' = im_stdif_raw image.value target_mean_weight.value target_mean target_deviation_weight.value target_deviation window_size window_size; off = (window_size + 1) / 2; } } } #separator /* various ideal fourier filters */ Ideal_filter = class { _preview_size = 64; _high_low image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value 0 0 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value 0 0 0 0; } _ring image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 6; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value ring_width.value 0 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value ring_width.value 0 0 0; } _band image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 12; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; } /* high or low pass ideal filter */ High_low x = map_unary _high_low x; /* ring pass or reject ideal filter */ Ring x = map_unary _ring x; /* band pass or reject ideal filter */ Band x = map_unary _band x; } /* Gaussian fourier filters */ Gaussian_filter = class { _preview_size = 64; _high_low image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 4; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value amplitude_cutoff.value 0 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value amplitude_cutoff.value 0 0 0; } _ring image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 10; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; } value = im_flt_image_freq image.value _type frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; } _band image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 16; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; } value = im_flt_image_freq image.value _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; } /* high or low pass Gaussian filter */ High_low x = map_unary _high_low x; /* ring pass or reject Gaussian filter */ Ring x = map_unary _ring x; /* band pass or reject Gaussian filter */ Band x = map_unary _band x; } /* various Butterworth fourier filters */ Butterworth_filter = class { _preview_size = 64; _high_low image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 2; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type order.value frequency_cutoff.value amplitude_cutoff.value 0 0; } value = im_flt_image_freq image.value _type order.value frequency_cutoff.value amplitude_cutoff.value 0 0; } _ring image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 8; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; } value = im_flt_image_freq image.value _type order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; } _band image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; order = Slider 1 10 2; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 14; visualize_mask = Image vis { vis = image_set_type Image_type.FOURIER (rotquad mask); mask = im_create_fmask _preview_size _preview_size _type order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; } value = im_flt_image_freq image.value _type order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; } /* high or low pass Butterworth filter */ High_low x = map_unary _high_low x; /* ring pass or reject Butterworth filter */ Ring x = map_unary _ring x; /* band pass or reject Butterworth filter */ Band x = map_unary _band x; } ================================================ FILE: share/nip2/compat/7.9/Format.def ================================================ /* various operations to convert numeric precision */ Convert_format_to = class { /* convert to unsigned 8 bit [0, 255] */ unsigned_8bit x = map_unary cast_unsigned_char x; /* convert to signed 8 bit [-128, 127] */ signed_8bit x = map_unary cast_signed_char x; /* convert to unsigned 16 bit [0, 65535] */ unsigned_16bit x = map_unary cast_unsigned_short x; /* convert to signed 16 bit [-32768, 32767] */ signed_16bit x = map_unary cast_signed_short x; /* convert to unsigned 32 bit [0, 4294967295] */ unsigned_32bit x = map_unary cast_unsigned_int x; /* convert to signed 32 bit [-2147483648, 2147483647] */ signed_32bit x = map_unary cast_signed_int x; /* convert to IEEE 32 bit floating point */ float_32bit x = map_unary cast_float x; /* convert to IEEE 64 bit floating point */ float_64bit x = map_unary cast_double x; /* convert to 64 bit complex (2 x IEEE 32 bit floating point) */ complex_64bit x = map_unary cast_complex x; /* convert to 128 bit complex (2 x IEEE 64 bit floating point) */ complex_128bit x = map_unary cast_double_complex x; } /* linear scale of pixel values to fit range 0-255 */ Scale_to_byte x = map_unary scale x; #separator /* try to make a matrix out of an object */ Convert_to_matrix in = map_unary to_matrix in; /* try to make an image out of an object */ Convert_to_image in = map_unary to_image in; #separator /* measure average value of a set of patches */ Matrix_from_colour_chart x = map_unary chart x { chart image = class Matrix value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; pacross = 6; pdown = 4; value = (im_measure image.value 0 0 image.width image.height pacross pdown).value; /* Hmm, not very helpful, we throw edits away. */ Matrix_vips_edit value scale offset filename display = chart image; } } /* make a synthetic colour chart image from a matrix */ Colour_chart_from_matrix matrix = map_unary build_chart matrix { build_chart matrix = class Image value { _check_args = [ [matrix, "matrix", check_Matrix] ] ++ super._check_args; patches_across = 6; patches_down = 4; patch_width = 50; patch_height = 50; border_width = 0; value = imagearray_assemble overlap overlap patch_table { overlap = -border_width; // patch numbers for row starts rowstart = map (multiply patches_across) [0 .. patches_down - 1]; // assemble patches ... each one a pixel value patches = map (take patches_across) (map (converse drop matrix.value) rowstart); // make an n-band constant image from eg. [1,2,3] patch v = image_new patch_width patch_height (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } #separator /* make a colour from the average values in an image */ Colour_from_image image = map_unary test image { test x = build_chart x, is_instanceof "Image" x = build_chart (Image pixel), is_instanceof "Point" x = error ("Colour_from_image: arg not Image or Point: " ++ print x) { pixel = x.image.extract_area x.left x.top 1 1; } build_chart image = class Colour colour_space value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; colour_space = table.lookup 1 0 type, table.present 1 type = error ("Colour_from_image: unable to make Colour " ++ "from " ++ Image_type.type_names.lookup 1 0 type ++ " image") { table = Image_type.colour_spaces; type = im_header_int "Type" image.value; } value = map mean (bandsplit image.value); } } /* make a constant image from a colour patch */ Image_from_colour x = map_unary build x { build c = class Image value { _check_args = [ [c, "c", check_Colour] ] ++ super._check_args; width = 64; height = 64; value = image_new width height 3 Image_format.FLOAT Image_coding.NOCODING (get_type c) c 0 0; } } #separator /* try to break object in into a list of components */ Decompose in = map_unary dec in { dec x = bandsplit x, is_instanceof "Image" x = map Vector x.value, is_instanceof "Matrix_base" x = x.value, is_instanceof "Vector" x || is_instanceof "Real" x = error "Decompose: not Image/Matrix/Vector/Real"; } /* try to put a list of objects together into a large single object */ Compose x = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof (is_instanceof "Image") x = Vector (map get_value x), is_listof (is_instanceof "Real") x = Matrix (map get_value x), is_listof (is_instanceof "Vector") x = map_unary Compose x, is_list x = error "Compose: not list of Image/Vector/Real/image/real" { get_value x = x.value; } ================================================ FILE: share/nip2/compat/7.9/Histogram.def ================================================ /* find histogram of x */ Hist_find x = map_unary hist_find x; /* find n-dimensional histogram from n-band image */ Hist_find_nD in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; // default to something small-ish bins = 8; value = hist_find_nD bins image.value; } } /* map image x through histogram hist */ Hist_map hist x = map_binary hist_map hist x; /* find cumulative histogram of hist */ Hist_cumulative x = map_unary hist_cum x; /* find normalised histogram of hist */ Hist_normalise x = map_unary hist_norm x; /* find LUT which matches hist in to hist ref */ Hist_match in ref = map_binary hist_match in ref; #separator /* histogram equalisation */ Hist_equalise = class { /* histogram equalise x globally */ Global x = map_unary hist_equalize x; /* local histogram equalisation using region r as window */ Local r = map_unary (hist_equalize_local r.width r.height) r.image; } /* slice a line of pixels along a guide line and display as a histogram */ Guide_slice in = map_unary widget in { widget guide = class Image value { _check_args = [ [guide, "Guide", check_Guide] ] ++ super._check_args; value = image_set_type Image_type.HISTOGRAM slice { slice = guide.image.extract_area guide.image.rect.left guide.top guide.width 1, is_instanceof "HGuide" guide = guide.image.extract_area guide.left guide.image.rect.top 1 guide.height; } } } ================================================ FILE: share/nip2/compat/7.9/Image.def ================================================ /* take a copy of x */ Duplicate x = map_unary copy x; /* crop image x */ Crop x = map_unary build_widget x { build_widget image = widget image (image.width * 0.25 - image.xoffset) (image.height * 0.25 - image.yoffset) (image.width * 0.5) (image.height * 0.5); widget image left top width height = class Region image left top width height { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 4; Region_edit image left top width height = widget image left top width height; } } /* insert image b into image a */ Insert a b = class Image value { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _check_all = [ [a.format == b.format && a.coding == b.coding && a.bands == b.bands, "a.format == b.format && a.coding == b.coding && " ++ "a.bands == b.bands"], [a.width >= b.width && a.height >= b.height, "First image should be able to enclose second"] ]; _vislevel = 3; place = Area a ((a.width - b.width) / 2) ((a.height - b.height) / 2) b.width b.height; value = im_insert_noexpand a' b'' place.left place.top { a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; } } /* join two images left/right or up/down */ Join = class { _check_ab_args a b = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_ab_all a b = [ [a.format == b.format && a.coding == b.coding && a.bands == b.bands, "a.format == b.format && a.coding == b.coding && " ++ "a.bands == b.bands"] ]; /* join two images left-right */ Left_right a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; _check_all = _check_ab_all a b ++ super._check_all; shim = Slider 0 100 0; background_colour = 0; align = Option "Alignment" ["Top", "Centre", "Bottom"] 1; value = im2 { w = a.width + b.width + shim.value; h = max_pair a.height b.height; bg = image_new w h a.bands a.format a.coding a.type background_colour 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = im_insert_noexpand bg a.value 0 (ya?align); im2 = im_insert_noexpand im1 b.value (a.width + shim.value) (yb?align); } } /* join two images top-bottom */ Top_bottom a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; _check_all = _check_ab_all a b ++ super._check_all; shim = Slider 0 100 0; background_colour = 0; align = Option "Alignment" ["Left", "Centre", "Right"] 1; value = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim.value; bg = image_new w h a.bands a.format a.coding a.type background_colour 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = im_insert_noexpand bg a.value (xa?align) 0; im2 = im_insert_noexpand im1 b.value (xb?align) (a.height + shim.value); } } /* join a 2-D array of images */ Array x = class Image value { hspacing = Slider (-100) (100) 0; vspacing = Slider (-100) (100) 0; value = imagearray_assemble (-hspacing.value) (-vspacing.value) x' { x' = map (map getval) x; getval x = x.value, is_class x = x; } } } /* morph images to match (needs the rubbersheet plugin) */ Rubber = class { _rubber_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.bilinear; _rubber_order = Option "order" ["0", "1", "2", "3"] 1; _rubber_edges = Toggle "Wrap image edges" false; /* find a transform which will turn sample image into reference image */ Rubber_find reference sample = class Matrix transformation { _vislevel = 3; // controls order = _rubber_order; interp = _rubber_interp; edges = _rubber_edges; max_error = 0.3; max_iterations = 10; // transform _result = resample sample.value reference.value max_error max_iterations order.value interp.value edges.value; // results transformed_image = Image (_result?0); transformation = (_result?1).value; final_error = _result?2; } /* apply a transform to an image */ Rubber_apply transform image = class Image value { // controls interp = _rubber_interp; edges = _rubber_edges; value = im_transform image.value transform interp.value edges.value; } /* change a transformation's scale factor */ Rubber_scale transform = class Matrix scaled_transform { factor_hint = "scale transform by this factor"; factor_x = 1; factor_y = 1; // pairwise multiply scaled_transform = map2 (map2 multiply) transform.value facs { facs = [[ factor_x, factor_y ], [ 1, 1 ], [ 1, 1 ], [ 1 / factor_x, 1 / factor_y ], [ 1 / factor_x, 1 / factor_y ], [ 1 / factor_x, 1 / factor_y ]]; } } } #separator /* set pixels to 255 - x */ Photographic_negative x = map_unary invert x { invert x = oo_unary_function neg_op x, is_class x = im_invert x, is_image x = 255 - x, is_number x = error (errors.badargs ++ "invert"); neg_op = Operator "photographic_negative" invert Operator_type.ARITHMETIC false; } /* falsecolour a mono image */ Falsecolour x = map_unary falsecolour x; #separator /* adjust brightness and contrast of image x */ Adjust_scale_offset x = map_unary widget x { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; scale = Slider 0.001 255 1; offset = Slider (-128) 128 0; value = clip2fmt image.format (image * scale + offset).value; } } /* adjust gamma of image x */ Adjust_gamma x = map_unary widget x { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; gamma = Slider 0.001 4 1; image_maximum_hint = "Change image_maximum if this is " ++ "not an 8 bit image"; image_maximum = 255; value = clip2fmt image.format gammaed.value { gammaed = (image_maximum / image_maximum ** gamma) * image ** gamma; } } } /* change advisory header fields of image x */ Edit_header x = map_unary widget x { type_names = Image_type.type_names; names = sort (map (extract 0) type_names.value); widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; xres = image.xres; yres = image.yres; xoffset = image.xoffset; yoffset = image.yoffset; type_option = Option "Image type" names pos { name = type_names.lookup 1 0 image.type; pos = index (equal name) names; } value = im_copy_set image.value type xres yres xoffset yoffset { type = type_names.lookup 0 1 names?type_option; } } } #separator /* rotate and scale one image to match another */ Match a b = class Image value { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _vislevel = 3; ap1 = Point_relative a 0.5 0.25; bp1 = Point_relative b 0.5 0.25; ap2 = Point_relative a 0.5 0.75; bp2 = Point_relative b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; value = b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; ap1' = ap1.image_rect; ap2' = ap2.image_rect; bp1' = bp1.image_rect; bp2' = bp2.image_rect; b''' = im_match_linear_search a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2'.left ap2'.top bp2'.left bp2'.top object window, refine = im_match_linear a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2'.left ap2'.top bp2'.left bp2'.top; } } /* make a colour overlay of two mono images */ Overlay a b = class Image value { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ] ++ super._check_args; _check_all = [ [a.bands == 1 && b.bands == 1, "a.bands == 1 && b.bands == 1"] ] ++ super._check_all; _vislevel = 3; ap1 = Point_relative a 0.5 0.25; bp1 = Point_relative b 0.5 0.25; ap2 = Point_relative a 0.5 0.75; bp2 = Point_relative b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; colour = Option "Colour overlay as" [ "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; value = [(a' ++ b''' ++ black), (a' ++ black ++ b'''), (b''' ++ a' ++ black), (b''' ++ black ++ a'), (black ++ a' ++ b'''), (black ++ b''' ++ a')]?colour { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; ap1' = ap1.image_rect; ap2' = ap2.image_rect; bp1' = bp1.image_rect; bp2' = bp2.image_rect; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1' ap2'; bp2'' = norm bp1' bp2'; b''' = im_match_linear_search a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1'.left ap1'.top bp1'.left bp1'.top ap2''.left ap2''.top bp2''.left bp2''.top; black = image_new a.width a.height a.bands a.format a.coding a.type 0 0 0; } } /* browse through the bands of a multiband image with a slider */ Browse_multiband image = class Image value { _vislevel = 3; band = Slider 0 (image.bands - 1) 0; display = Option "Display as" [ "Grey", "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; value = output { down = (int) band.value; up = (int) (band.value + 1); remainder = band.value - down; a = (image.value ? up) * remainder; b = (image.value ? down) * (1 - remainder); c = image_new image.width image.height 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; output = [ a + b, a ++ b ++ c, a ++ c ++ b, b ++ a ++ c, b ++ c ++ a, c ++ a ++ b, c ++ b ++ a ] ? display; } } ================================================ FILE: share/nip2/compat/7.9/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/7.9 start_DATA = \ Math.def \ Image.def \ Mosaic.def \ Colour.def \ Resize.def \ Capture.def \ Format.def \ Filter.def \ Morphology.def \ New.def \ Histogram.def \ Print.def \ Rotate.def \ Statistics.def \ X_ray.def \ _convert.def \ _errors.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/7.9/Math.def ================================================ /* basic arithmetic for objects */ Arithmetic = class { /* add a and b */ Add a b = map_binary add a b; /* subtract b from a */ Subtract a b = map_binary subtract a b; /* multiply a by b */ Multiply a b = map_binary multiply a b; /* divide a by b */ Divide a b = map_binary divide a b; /* remainder after dividing a by b */ Remainder a b = map_binary remainder a b; sep1 = Separator; /* absolute value of x */ Absolute_value x = map_unary abs x; /* like Absolute_value, but treat pixels as vectors */ Absolute_value_vector x = map_unary abs_vec x; /* calculate unit vector for band elements */ Sign x = map_unary sign x; /* multiply by -1 */ Negate x = map_unary unary_minus x; } /* trigonometry operations (all in degrees) */ Trig = class { /* calculate sine x */ Sin x = map_unary sin x; /* calculate cosine x */ Cos x = map_unary cos x; /* calculate tangent x */ Tan x = map_unary tan x; sep1 = Separator; /* calculate arc sine x */ Asin x = map_unary asin x; /* calculate arc cosine x */ Acos x = map_unary acos x; /* calculate arc tangent x */ Atan x = map_unary atan x; sep2 = Separator; /* convert degrees to radians */ Rad x = map_unary rad x; /* convert radians to degrees */ Deg x = map_unary deg x; sep3 = Separator; /* is angle within t degrees of r, mod 360 */ Angle_range t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } /* logarithms and anti-logs */ Log = class { /* calculate e ** x */ Exponential x = map_unary (power e) x; /* log base e of x */ Log_natural x = map_unary log x; sep1 = Separator; /* log base 10 of x */ Log10 x = map_unary log10 x; /* calculate 10 ** x */ Exponential10 x = map_unary (power 10) x; sep2 = Separator; /* calculate x ** y */ Raise_to_power x y = map_binary power x y; } /* operations on complex numbers and images */ Complex = class { /* extract fields from complex */ Complex_extract = class { /* extract real part of complex */ Real in = map_unary re in; /* extract imaginary part of complex */ Imaginary in = map_unary im in; } /* join a and b to make a complex */ Complex_build a b = map_binary comma a b; sep1 = Separator; /* convert real and imag to amplitude and phase */ Polar a = map_unary polar a; /* convert (amplitude, phase) image to rectangular coordinates */ Rectangular x = map_unary rectangular x; sep2 = Separator; /* invert imaginary part */ Conjugate x = map_unary conj x; } /* bitwise boolean operations for integer objects */ Boolean = class { /* bitwise and of a and b */ And a b = map_binary bitwise_and a b; /* bitwise or of a and b */ Or a b = map_binary bitwise_or a b; /* bitwise exclusive or of a and b */ Eor a b = map_binary eor a b; /* invert a */ Not a = map_unary not a; sep1 = Separator; /* shift a right by b bits */ Right_shift a b = map_binary right_shift a b; /* shift a left by b bits */ Left_shift a b = map_binary left_shift a b; sep2 = Separator; /* b where a is non-zero, c elsewhere */ If_then_else a b c = map_trinary if_then_else a b c; /* or the bands of an image together */ Band_or im = foldr1 bitwise_or (bandsplit im); /* and the bands of an image together */ Band_and im = foldr1 bitwise_and (bandsplit im); } /* all comparison operations */ Relational = class { /* find points at which a is equal to b */ Equal a b = map_binary equal a b; /* find points at which a is not equal to b */ Not_equal a b = map_binary not_equal a b; /* find points at which a is greater than b */ More a b = map_binary more a b; /* find points at which a is less than b */ Less a b = map_binary less a b; /* find points at which a is greater than or equal to b */ More_equal a b = map_binary more_equal a b; /* find points at which a is less than or equal to b */ Less_equal a b = map_binary less_equal a b; } /* operations on lists */ List = class { /* take first element of list */ Head x = hd x; /* drop the first element of list */ Tail x = tl x; /* drop the last element of list */ Init x = init x; /* take the last element of list */ Last x = last x; sep1 = Separator; /* reverse order of elements in list */ Reverse x = reverse x; /* sort elements of list into ascending order */ Sort x = sort x; /* remove duplicates from list */ Make_set x = mkset equal x; /* exchange rows and columns in a list of lists */ Transpose_list x = transpose x; /* flatten a list of lists into a single list */ Concat l = concat l; sep2 = Separator; /* find the length of list */ Length x = len x; /* return element n from list (index from zero) */ Subscript n x = n ? x; /* take the first n elements of list x */ Take n x = take n x; /* drop the first n elements of list x */ Drop n x = drop n x; sep3 = Separator; /* join two lists end to end */ Join a b = a ++ b; /* put element a on the front of list x */ Cons a x = a : x; /* join two lists, pairwise */ Zip a b = zip2 a b; } /* various rounding operations */ Round = class { /* smallest integral value not less than x */ Ceil x = map_unary ceil x; /* largest integral value not greater than x */ Floor x = map_unary floor x; /* round to nearest integer */ Rint x = map_unary rint x; } /* forward and reverse fourier transforms */ Fourier = class { /* find fourier transform of image */ Forward a = map_unary (rotquad @ fwfft) a; /* find inverse fourier transform of image */ Reverse a = map_unary (invfft @ rotquad) a; /* rotate quadrants */ Rotate_quadrants a = map_unary rotquad a; } ================================================ FILE: share/nip2/compat/7.9/Morphology.def ================================================ /* Some useful masks. */ _morph_mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; _morph_mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; _morph_mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; _morph_thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; /* dilate x with 8-connected mask */ Dilate8 x = map_unary (dilate _morph_mask8) x; /* dilate x with 4-connected mask */ Dilate4 x = map_unary (dilate _morph_mask4) x; /* erode x with 8-connected mask */ Erode8 x = map_unary (erode _morph_mask8) x; /* erode x with 4-connected mask */ Erode4 x = map_unary (erode _morph_mask4) x; #separator /* open with 8-connected mask */ Open x = map_unary (dilate _morph_mask8 @ erode _morph_mask8) x; /* close with 8-connected mask */ Close x = map_unary (erode _morph_mask8 @ dilate _morph_mask8) x; /* remove single points */ Clean x = map_unary clean x { clean x = x ^ erode _morph_mask1 x; } /* thin once */ Thin x = map_unary thinall x { masks = take 8 (iterate rot45 _morph_thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } #separator /* dilate object x with mask m */ Dilate m x = map_unary (dilate m) x; /* erode object x with mask m */ Erode m x = map_unary (erode m) x; /* dilate mask m with itself n times */ Dilate_multiple m n = (iterate (dilate m) m)?n; /* erode mask m with itself n times */ Erode_multiple m n = (iterate (erode m) m)?n; #separator /* morph with a mask you can edit */ Custom_morphology in = map_unary morph in { morph image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; mask = _morph_mask8; type = Option "Operation" ["Erode", "Dilate"] 1; hint_apply_n_times = "Number of times to apply mask:"; apply_n_times = 1; border = Toggle "Output image matches input image in size" true; value = im_embed image' 0 xoff yoff image.width image.height, border = image' { fatmask = (iterate (dilate mask) mask) ? (apply_n_times - 1); image' = im_erode_raw image.value fatmask, type.value == 0 = im_dilate_raw image.value fatmask; xoff = (fatmask.width + 1) / 2; yoff = (fatmask.height + 1) / 2; } } } /* search in from the edges of an image for the first non-zero pixel */ Find_profile in = map_unary widget in { widget image = class Image value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; value = image_set_type Image_type.HISTOGRAM [ im_profile image.value 0, rot270 (im_profile image.value 1), im_profile (flipud image.value) 0, rot270 (im_profile (fliplr image.value) 1) ]?edge; } } /* count the average number of black-to-white transitions across an image */ Count_lines in = map_unary widget in { widget image = class Real value { _check_args = [ [image, "Image", check_Image] ] ++ super._check_args; _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; value = im_cntlines image.value edge.value; } } ================================================ FILE: share/nip2/compat/7.9/Mosaic.def ================================================ /* Check and group a point list by image. */ _mosaic_sort_test l = error "mosaic: not all points", !is_listof (is_instanceof "Point") l = error "mosaic: points not on two images", len images != 2 = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = land (map (equal (hd l)) (tl l)); get_format x = x.image.format; get_coding x = x.image.coding; // all the different images get_image x = x.image; images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = p.image === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to get * above before below. */ _mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = (a?0).left > (b?0).left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to get * left before right. */ _mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = (a?0).top > (b?0).top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ _mosaic_sort fn = concat @ transpose @ fn @ _mosaic_sort_test; /* translate and blend two images together left/right or up/down */ Mosaic_translate = class { _check_ab_args a b = [ [a, "a", check_Point], [b, "b", check_Point] ]; // shortcut to prefs _prefs = Workspaces.Preferences; _search_area = _prefs.MOSAIC_WINDOW_SIZE; _object_size = _prefs.MOSAIC_OBJECT_SIZE; _blend_width = Slider 0 100 _prefs.MOSAIC_MAX_BLEND_WIDTH; _refine = Toggle "Refine selected tie-points" _prefs.MOSAIC_REFINE; /* translate and blend two images left/right */ Left_right a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_lrmosaic a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_lrmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_lr [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } /* translate and blend two images top/bottom */ Top_bottom a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_tbmosaic a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_tbmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_tb [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } } /* forcibly translate and blend two images together left/right or up/down */ Mosaic_force = class { _check_ab_args a b = [ [a, "a", check_Point], [b, "b", check_Point] ]; // shortcut to prefs _prefs = Workspaces.Preferences; _blend_width = Slider 0 100 _prefs.MOSAIC_MAX_BLEND_WIDTH; /* forcibly translate and blend two images left/right */ Left_right a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; value = im_lrmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_lr [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } /* forcibly translate and blend two images top/bottom */ Top_bottom a b = class Image value { _check_args = _check_ab_args a b ++ super._check_args; blend_width = _blend_width; value = im_tbmerge a'.image.value b'.image.value (rb'.left - ra'.left) (rb'.top - ra'.top) blend_width.value { sorted = _mosaic_sort _mosaic_sort_tb [a, b]; a' = sorted?0; b' = sorted?1; ra' = a'.image_rect; rb' = b'.image_rect; } } } /* translate, rotate, scale and blend two images together left/right or up/down */ Mosaic_affine = class { _check_abcd_args a b c d = [ [a, "a", check_Point], [b, "b", check_Point], [c, "c", check_Point], [d, "d", check_Point] ]; // shortcut to prefs _prefs = Workspaces.Preferences; _search_area = _prefs.MOSAIC_WINDOW_SIZE; _object_size = _prefs.MOSAIC_OBJECT_SIZE; _blend_width = Slider 0 100 _prefs.MOSAIC_MAX_BLEND_WIDTH; _refine = Toggle "Refine selected tie-points" _prefs.MOSAIC_REFINE; /* translate, rotate, scale and blend two images left/right */ Left_right a b c d = class Image value { _check_args = _check_abcd_args a b c d ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_lrmosaic1 a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_lrmerge1 a'.image.value b'.image.value ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top blend_width.value { sorted = _mosaic_sort _mosaic_sort_lr [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; ra' = a'.image_rect; rb' = b'.image_rect; rc' = c'.image_rect; rd' = d'.image_rect; } } /* translate, rotate, scale and blend two images top/bottom */ Top_bottom a b c d = class Image value { _check_args = _check_abcd_args a b c d ++ super._check_args; blend_width = _blend_width; refine = _refine; value = im_tbmosaic1 a'.image.value b'.image.value 0 ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top (_object_size / 2) (_search_area / 2) 0 blend_width.value, refine = im_tbmerge1 a'.image.value b'.image.value ra'.left ra'.top rb'.left rb'.top rc'.left rc'.top rd'.left rd'.top blend_width.value { sorted = _mosaic_sort _mosaic_sort_tb [a, b, c, d]; a' = sorted?0; b' = sorted?1; c' = sorted?2; d' = sorted?3; ra' = a'.image_rect; rb' = b'.image_rect; rc' = c'.image_rect; rd' = d'.image_rect; } } } #separator /* disassemble mosaic, adjust brightnesses to match, and reassemble */ Mosaic_balance x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (errors.badargs ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } /* brighten along a left/right or up/down axis */ Tilt_brightness = class { /* brighten along a left/right axis */ Left_right x = map_unary tilt_lr x { tilt_lr image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; tilt = Slider (-1) 1 0; value = final { ramp = im_fgrey image.width image.height; ramp' = bandjoin (map (const ramp) [1..image.bands]); scale = (ramp' - 0.5) * tilt + 1; final = image.value * scale; } } } /* brighten along a top/bottom axis */ Top_bottom x = map_unary tilt_tb x { tilt_tb image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; tilt = Slider (-1) 1 0; value = final { ramp = rot90 (im_fgrey image.height image.width); ramp' = bandjoin (map (const ramp) [1..image.bands]); scale = (ramp' - 0.5) * tilt + 1; final = image.value * scale; } } } } #separator /* disassemble mosaic, substitute different image files, and reassemble */ Mosaic_rebuild x = map_unary remosaic x { remosaic image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; old_hint = "For the name of each file making up the mosaic, " ++ "exchange this string:"; old = "foo"; new_hint = "for this string:"; new = "bar"; value = im_remosaic image.value old new; } } ================================================ FILE: share/nip2/compat/7.9/New.def ================================================ /* make a new blank image */ New_image = widget { type_names = Image_type.type_names; names = sort (map (extract 0) type_names.value); default_type_name = type_names.lookup 1 0 Image_type.MULTIBAND; default_type_pos = index (equal default_type_name) names; widget = class Image value { width = 64; height = 64; bands = 1; format_option = Option "Image format" [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ] 0; type_option = Option "Image type" names default_type_pos; value = image_new width height bands format Image_coding.NOCODING type 0 0 0 { format = format_option.value; type = type_names.lookup 0 1 names?type_option; } } } /* pick a colour ... any colour space */ New_colour = widget "Lab" [50,0,0] { widget default_colour value = class Colour colour_space value { _colourspaces = [ "XYZ", "Yxy", "Lab", "LCh", "UCS", "RGB", "sRGB" ]; colour_space_option = Option "Colour space" _colourspaces (index (equal default_colour) _colourspaces); colour_space = colour_space_option.labels? colour_space_option.value; Colour_edit colour_space value = widget colour_space value; } } /* make a slider */ New_slider = Slider 0 255 128; /* make a toggle widget */ New_toggle = Toggle "untitled" false; /* make an option widget */ New_option = Option "untitled" ["option0", "option1"] 0; /* make a string entry widget */ New_string = String "Enter a string" "sample text"; /* make a number entry widget */ New_number = Number "Enter a number" 42; /* make a file chooser */ New_filename = Filename "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; /* make a matrix */ New_matrix = class { /* make a plain matrix */ Plain = Matrix (identity_matrix 3); /* make a convolution matrix */ Convolution = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; /* make a recombination matrix */ Recombination = Matrix_rec (identity_matrix 3); /* make a morphology matrix */ Morphology = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } /* make a mark on an image */ New_mark = class { /* mark a region on an image */ Region image = scope.Region_relative image 0.25 0.25 0.5 0.5; /* mark a point on an image */ Point image = scope.Point_relative image 0.5 0.5; /* mark an arrow on an image */ Arrow image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; /* mark a horizontal guide on an image */ HGuide image = scope.HGuide_relative image 0.5; /* mark a vertical guide on an image */ VGuide image = scope.VGuide_relative image 0.5; } #separator /* make a spatial response pattern image */ New_eye = class Image value { width = 64; height = 64; factor = Slider 0.001 1 0.2; value = im_eye width height factor.value; } /* make a zone plate image */ New_zone_plate = class Image value { size = 64; value = im_zone size; } /* make a grey ramp image */ New_grey = class Image value { width = 64; height = 64; orientation = Option "" ["Horizontal", "Vertical"] 0; value = [im_grey width height, im_rot90 (im_grey height width)]?orientation; } /* make a two band image whose pixel values are their coordinates */ New_xy = class Image value { width = 64; height = 64; value = make_xy width height; } /* make a new image of gaussian noise */ New_gauss_noise = class Image value { size = 64; mean = Slider 0 255 128; deviation = Slider 0 128 50; value = im_gaussnoise size size mean.value deviation.value; } /* make a 2d fractal image */ New_fractal = class Image value { size = 64; dimension = Slider 2.001 2.999 2.001; value = im_fractsurf size dimension.value; } /* make a CRT calibration chart */ New_CRT_test_chart = class Image value { brightness = Slider 0 255 200; patch_size = 32; value = imagearray_assemble 0 0 [[green, red], [blue, white]] { black = image_new patch_size patch_size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } /* make a frequency test chart */ New_frequency_test_chart = class Image value { width = 64; strip_height = 10; wavelengths = [64, 32, 16, 8, 4, 2]; value = join.value { freq_slice wave = Image (sin ((im_fgrey width strip_height) * 360 * width / wave) > 0); strips = map freq_slice wavelengths; join = foldl1 Join.Top_bottom strips; } } /* make a checkerboard pattern */ New_checkerboard = class Image value { width = 64; height = 64; horizontal_patch_size = 8; vertical_patch_size = 8; horizontal_patch_offset = 0; vertical_patch_offset = 0; value = xstripes ^ ystripes { pixels = make_xy width height; xpixels = pixels?0 + horizontal_patch_offset; ypixels = pixels?1 + vertical_patch_offset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels horizontal_patch_size; ystripes = make_stripe ypixels vertical_patch_size; } } /* make a grid pattern */ New_grid = class Image value { width = 64; height = 64; horizontal_line_spacing = 8; vertical_line_spacing = 8; line_thickness = 1; horizontal_grid_offset = 0; vertical_grid_offset = 0; value = xstripes | ystripes { pixels = make_xy width height; xpixels = pixels?0 + horizontal_grid_offset; ypixels = pixels?1 + vertical_grid_offset; make_stripe pix swidth = pix % swidth < line_thickness; xstripes = make_stripe xpixels horizontal_line_spacing; ystripes = make_stripe ypixels vertical_line_spacing; } } #separator /* make a gaussian matrix */ New_gauss_matrix = class Matrix_vips _mask.value _mask.scale _mask.offset _mask.filename _mask.display { sigma = Slider 0.001 10 1; min_amplitude = Slider 0 1 0.2; integer = Toggle "Integer" false; _mask = fn sigma.value min_amplitude.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } /* make a laplacian of a gaussian mask */ New_log_matrix = class Matrix_vips _mask.value _mask.scale _mask.offset _mask.filename _mask.display { sigma = Slider 0.001 10 1.5; min_amplitude = Slider 0 1 0.1; integer = Toggle "Integer" false; _mask = fn sigma.value min_amplitude.value { fn = im_log_imask, integer = im_log_dmask; } } #separator /* make the mask images for various ideal fourier filters */ New_ideal = class { /* make a mask image for an ideal highpass/lowpass fourier filter */ High_low = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value 0 0 0 0; } } /* make a mask image for an ideal ring pass/reject fourier filter */ Ring = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 6; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value ring_width.value 0 0 0; } } /* make a mask image for an ideal band pass/reject fourier filter */ Band = class Image value { size = 64; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 12; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value 0 0; } } } /* various Gaussian fourier filters */ New_gaussian = class { /* make a mask image for a gaussian highpass/lowpass fourier filter */ High_low = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 4; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value amplitude_cutoff.value 0 0 0; } } /* make a mask image for a gaussian ring pass/reject fourier filter */ Ring = class Image value { size = 64; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 10; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff.value ring_width.value amplitude_cutoff.value 0 0; } } /* make a mask image for a gaussian band pass/reject fourier filter */ Band = class Image value { size = 64; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 16; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value 0; } } } /* various Butterworth fourier filters */ New_butterworth = class { /* make a mask image for a Butterworth highpass/lowpass fourier filter */ High_low = class Image value { size = 64; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "High or low" ["High pass", "Low pass"] 0; _type = type.value + 2; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type order.value frequency_cutoff.value amplitude_cutoff.value 0 0; } } /* make a mask image for a Butterworth ring pass/reject fourier filter */ Ring = class Image value { size = 64; order = Slider 1 10 2; frequency_cutoff = Slider 0.01 0.99 0.5; ring_width = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Ring pass", "Ring reject"] 0; _type = type.value + 8; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type order.value frequency_cutoff.value ring_width.value amplitude_cutoff.value 0; } } /* make a mask image for a Butterworth band pass/reject fourier filter */ Band = class Image value { size = 64; order = Slider 1 10 2; frequency_cutoff_x = Slider 0.01 0.99 0.5; frequency_cutoff_y = Slider 0.01 0.99 0.5; radius = Slider 0.01 0.99 0.5; amplitude_cutoff = Slider 0.01 0.99 0.5; type = Option "Pass or reject" ["Band pass", "Band reject"] 0; _type = type.value + 14; value = image_set_type Image_type.FOURIER (rotquad mask) { mask = im_create_fmask size size _type order.value frequency_cutoff_x.value frequency_cutoff_y.value radius.value amplitude_cutoff.value; } } } #separator /* make a slice through CIELAB space */ New_CIELAB_slice = class Image value { size = 64; L = Slider 0 100 50; value = lab_slice size L.value; } /* pick a colour in LAB space */ New_LAB_colour = widget "Lab" [50, 0, 0] { // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range); ab2xy a = (a * (size / range)); widget space default_value = class Colour space value { L = default_value?0; a = default_value?1; b = default_value?2; lightness = Slider 0 100 L; ab_slice = Image (lab_slice size lightness.value); point = Point ab_slice (ab2xy a) (ab2xy b); value = [lightness.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } ================================================ FILE: share/nip2/compat/7.9/Print.def ================================================ /* cored sharpen of L only in LAB image ... tuned for typical printers */ Sharpen_for_print in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "300 dpi", "150 dpi", "75 dpi" ] 0; // sharpen params for 300, 150 and 75 dpi // just change the size of the area we search _param_table = [ [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] ]; _params = _param_table?target_dpi; value = im_sharpen (colour_transform_to Image_type.LABQ in.value) _params?0 _params?1 _params?2 _params?3 _params?4 _params?5; } } /* adjust tone curve on L* */ Tone_for_print in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; black = Slider 0 100 0; white = Slider 0 100 100; shadow_point = Slider 0.1 0.3 0.2; mid_point = Slider 0.4 0.6 0.5; highlight_point = Slider 0.7 0.9 0.8; shadow_adjust = Slider (-15) 15 0; mid_adjust = Slider (-30) 30 0; highlight_adjust = Slider (-15) 15 0; preview_curve = Image (im_tone_build black.value white.value shadow_point.value mid_point.value highlight_point.value shadow_adjust.value mid_adjust.value highlight_adjust.value); value = im_tone_map (colour_transform_to Image_type.LABQ in.value) preview_curve.value; } } /* morph image colours in LAB space ... useful for tweaking colour for print */ Morph_for_print in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; L_scale = 1.15; L_offset = -4.2; ab_scale = Slider 1 1.5 1.15; a_offset = Slider (-10) 10 0; b_offset = Slider (-10) 10 5; grey_correction = Matrix_con 1 0 [ [5, 5, -1 ], [10, 4, -1 ], [15, 2, -1 ], [20, 1, 1 ], [25, 1, 2 ], [30, 0, 1 ], [35, 0, 1 ], [40, 0, 1 ], [45, 0, 1 ], [50, 0, 1 ], [55, 0, 0 ], [99, 0, 0 ] ]; value = im_lab_morph in.value (Vector [0, a_offset.value, b_offset.value] + grey_correction) L_offset L_scale ab_scale.value ab_scale.value; } } #separator _sample_print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; _sample_monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; _render_intents = Option "Render intent" [ "Perceptual", "Relative", "Saturation", "Absolute" ] 3; /* transform from PCS to device space */ ICC_export in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; profile = Filename _sample_print_profile; intent = _render_intents; depth = Option "Output depth" ["8 bit", "16 bit"] 0; value = im_icc_export_depth in.value [8, 16]?depth (expand profile.value) intent.value; } } /* transform from device space to PCS */ ICC_import in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _check_all = [ [in.bands == 3 || in.bands == 4, "in.bands == 3 || in.bands == 4" ] ] ++ super._check_all; _vislevel = 3; profile = Filename _sample_monitor_profile, in.bands == 3 = Filename _sample_print_profile; intent = _render_intents; value = im_icc_import in.value (expand profile.value) intent.value; } } /* transform between two device spaces */ ICC_transform in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; in_profile = Filename _sample_monitor_profile; out_profile = Filename _sample_print_profile; intent = _render_intents; value = im_icc_transform in.value (expand in_profile.value) (expand out_profile.value) intent.value; } } /* transform from absolute to relative colorimetry */ ICC_ac2rc in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ] ++ super._check_args; _vislevel = 3; profile = Filename _sample_print_profile; value = im_icc_ac2rc in.value (expand profile.value); } } #separator /* convert between XYZ and Lab for D50 */ D50XYZ_to_D50Lab in = map_unary (colour_unary im_D50XYZ2Lab) in; /* convert between XYZ and Lab for D50 */ D50Lab_to_D50XYZ in = map_unary (colour_unary im_D50Lab2XYZ) in; #separator /* add an editable drop shadow to an image */ Drop_shadow x = map_unary shadow x { shadow image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; shadow_width = Slider 0 50 5; shadow_height = Slider 0 50 5; shadow_softness = Slider 0 20 5; use_mask = Toggle "Use mask to make shadow" false; mask_image = foldr1 bitwise_and (bandsplit (image > 128)); background_colour = 255; shadow_colour = 128; value = final { blur_size = shadow_softness.value * 2 + 1; // matrix we blur with to soften shadows mask_g = im_gauss_imask (blur_size / 3) 0.2; mask_g_line = mask_g.value ? (mask_g.height / 2); mask_g_sum = foldr1 add mask_g_line; blur_matrix = Matrix_con mask_g_sum 0 [mask_g_line]; mask_size = mask_g.width; // size of final image we build final_width = image.width + 2 * mask_size + shadow_width.value; final_height = image.height + 2 * mask_size + shadow_height.value; // make a plain image mk_background colour = image_new final_width final_height image.bands image.format Image_coding.NOCODING image.type colour 0 0; // make a mask image ... place at (x,y) in the final // image mk_mask x y = im_insert black mask_image.value x y, use_mask = im_insert black white x y { black = image_new final_width final_height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 0 0 0; white = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; } // make the shadow mask image ... offset mask and // soften shadow_mask = mk_mask (mask_size + shadow_width.value) (mask_size + shadow_height.value); shadow_mask' = im_convsep shadow_mask blur_matrix; // make underlay ... use shadow mask to blend between // background colour and shadow colour background = mk_background background_colour; shadow = mk_background shadow_colour; underlay = im_blend shadow_mask' shadow background; // overlay ... place image at final position overlay = mk_background 0; overlay' = im_insert overlay image.value mask_size mask_size; // overlay mask overlay_mask = mk_mask mask_size mask_size; final = if overlay_mask then overlay' else underlay; } } } ================================================ FILE: share/nip2/compat/7.9/Resize.def ================================================ _resize_interp = Option "Interpolation" (map (extract 0) Interpolate.names.value) Interpolate.bilinear; /* resize image x by any scale factor */ Resize_image x = map_unary widget x { widget image = class Image value { _vislevel = 3; factor = 1; interp = _resize_interp; value = resize factor factor interp.value image.value; } } /* resize image with separate x/y factors */ Resize_xy_image x = map_unary widget x { widget image = class Image value { _vislevel = 3; xfactor = 1; yfactor = 1; interp = _resize_interp; value = resize xfactor yfactor interp.value image.value; } } /* place image x in a larger piece of image */ Resize_canvas x = map_unary widget x { widget image = class Image value { _vislevel = 3; new_image_width = image.width; new_image_height = image.height; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east" ] 4; fill = Option "Fill background with" [ "White", "Black" ] 0; value = im_insert_noexpand background image.value xp yp { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; background_colour = image_white image, fill == 0 = Vector (map (const 0) [1 .. bands]); // placement vectors ... left, centre, right xposv = [0, new_image_width / 2 - width / 2, new_image_width - width]; yposv = [0, new_image_height / 2 - height / 2, new_image_height - height]; xp = xposv?((int) (position % 3)); yp = yposv?((int) (position / 3)); background = image_new new_image_width new_image_height bands format coding type background_colour 0 0; } } } #separator /* resize image x so that the shortest axis is a certain size */ Shrink_to = class { _shrink_width default_size image = class Image value { size = default_size; interp = _resize_interp; value = resize factor factor interp.value image.value { xfac = size / image.width; yfac = size / image.height; factor = max_pair xfac yfac; } } /* shrink minimum dimension to 400 pixels */ Quicklook x = map_unary (_shrink_width 400) x; /* shrink minimum dimension to 64 pixels */ Icon x = map_unary (_shrink_width 64) x; } ================================================ FILE: share/nip2/compat/7.9/Rotate.def ================================================ /* rotate images and matricies by fixed angles */ Rotate_fixed = class { /* rotate image clockwise in 90 degree increments */ _rotate_widget default a = map_unary widget a { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; value = [ image.value, rot90 image.value, rot180 image.value, rot270 image.value ] ? angle; } } /* clockwise rotate by 90 degrees */ R90 x = _rotate_widget 1 x; /* rotate by 180 degrees */ R180 x = _rotate_widget 2 x; /* clockwise rotate by 270 degrees */ R270 x = _rotate_widget 3 x; /* rotate by 45 degrees ... square, odd-length-sides, matrices only */ R45 x = map_unary rot45 x; } /* rotate image anticlockwise by any angle */ Rotate_free a = map_unary widget a { widget image = class Image value { _check_args = [ [image, "image", check_Image] ] ++ super._check_args; _vislevel = 3; angle = Slider 0 360 0; value = rotate angle image.value; } } #separator /* mirror left/right or up/down */ Flip = class { /* mirror object up/down */ Up_down x = map_unary flipud x; /* mirror object left/right */ Left_right x = map_unary fliplr x; } /* swap rows and columns */ Transpose x = map_unary transpose x; #separator /* smallest rotate that gets arrow vertical or horizontal */ Straighten_arrow x = map_unary straighten x { straighten arrow = rotate angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } ================================================ FILE: share/nip2/compat/7.9/Statistics.def ================================================ /* mean value of object */ Mean a = map_unary mean a; /* standard deviation of object */ Deviation a = map_unary deviation a; /* calculate a large set of stats on an object */ Stats a = map_unary stats a; #separator /* maximum of an object */ Max a = map_unary max a; /* minimum of an object */ Min a = map_unary min a; /* return complex number (max, min) */ Maxmin a = map_unary maxmin a { maxmin x = (Max x, Min x); } /* position of maximum in an image */ Maximum_position a = map_unary maxpos a; /* position of minimum in an image */ Minimum_position a = map_unary minpos a; #separator /* count the number of non-zeros in an image */ Count_set a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } /* count the number of zeros in an image */ Count_clear a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } #separator /* plot a scatter graph of a list of [x,y] coordinates */ Plot_scatter x //= map_unary widget x = widget x { widget data = class Image value { _check_args = [ [data, "data", check_xy_list] ] ++ super._check_args; width = 512; height = 512; plot_colour = Colour "Lab" [80, -80, 80]; xmin = foldr1 min_pair (map (extract 0) data); xmax = foldr1 max_pair (map (extract 0) data); ymin = foldr1 min_pair (map (extract 1) data); ymax = foldr1 max_pair (map (extract 1) data); axies = Toggle "Draw axies" true; mark = Point this x y { p = _to_image data?0; x = p?0; y = p?1; } mark_position_hint = "mark is at position:"; mark_position = _from_image [mark.left, mark.top]; // geometry _xrange = xmax - xmin; _yrange = ymax - ymin; _xscale = width / _xrange; _yscale = height / _yrange; // map an [x,y] point into the image coordinates _to_image p = [(p?0 - xmin) * _xscale, height - (p?1 - ymin) * _yscale]; // map an [x,y] point from image cods back to real cods _from_image p = [p?0 / _xscale + xmin, (height - p?1) / _yscale + ymin]; value = foldr plot background' data { plot_image_new width height pixel = image_new width height 3 Image_format.FLOAT Image_coding.NOCODING (Image_type.colour_spaces.lookup 0 1 plot_colour.colour_space) pixel 0 0; // background background = plot_image_new width height 0; // mark we plot mark_width = max_pair 1 (width / 100); mark_height = max_pair 1 (height / 100); mark = plot_image_new mark_width mark_height plot_colour; // draw axies on background background' = drawxy, axies = background { // axies xaxis = plot_image_new width 1 (Colour "Lab" [100, 0, 0]); yaxis = plot_image_new 1 height (Colour "Lab" [100, 0, 0]); origin = _to_image [0, 0]; drawx = im_insert_noexpand background xaxis 0 origin?1; drawxy = im_insert_noexpand drawx yaxis origin?0 0; } // plot a single point on an image plot p im = im_insert_noexpand im mark (x - mark_width / 2) (y - mark_height / 2) { p' = _to_image p; x = p'?0; y = p'?1; } } } } ================================================ FILE: share/nip2/compat/7.9/X_ray.def ================================================ /* replace dark or light section of im1 with section from im2 */ Replace_area im1 im2 = class Image value { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ] ++ super._check_args; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.1 0.1 0.1 0.1; /* Point on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Point im2 (im2.width * 0.1 - im2.xoffset) (im2.height * 0.1 - im2.yoffset); _r2 = Region im2 p2.left p2.top r1.width r1.height; _mask = [r1 <= Options.scale_cutoff, r1 >= Options.scale_cutoff] ? Options.control; mask = _mask?0; Options = option_1, format < 4 = option_2 { format = im1.format; option_1 = class { _vislevel = 3; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ control = Option "Removing a" [ "Dark Area", "Light Area" ] 1; /* Used to select the area to be replaced. */ scale_cutoff = Slider 0.01 mx (mx / 2) { mx = Image_format.maxval im1.format; } /* Option toggle how the levels in the replacment area are calculated. * Replacement with gaussian noise uses the scale&offset balancing. */ process = Option "Use" [ "Scale&Offset Balancing", "Gaussian noise replacement", "Histogram Balancing" ] 0; /* This allows the function to be paused. */ pause = Toggle "Pause function to allow easy adjustment of region r1." true; } option_2 = class { _vislevel = 3; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ control = Option "Removing a" [ "Dark Area", "Light Area" ] 1; /* Used to select the area to be replaced. */ scale_cutoff = Slider 0.01 mx (mx / 2) { /* the below function can not cope with floats * and don't need massive range for 32-bit ints so * will just define the max as for a 16 bit unsigned. */ mx = Image_format.maxval 2; //im1.format; } /* Option toggle how the levels in the replacment area are calculated. * Replacement with gaussian noise uses the scale&offset balancing. */ process = Option "Use" [ "Scale&Offset Balancing", "Gaussian noise replacement" ] 0; /* This allows the function to be paused. */ pause = Toggle "Pause function to allow easy adjustment of region r1." true; } } value = im1.value, Options.pause = im_insert im1.value patch r1.left r1.top { patch = _so_balance mask r1.value _r2.value false, Options.process == 0; = _so_balance mask r1.value _r2.value true, Options.process == 1; = _hist_balance_2 mask r1.value _r2.value; } }; #separator /* Balance the effect of secondary structure on an X-ray image * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference * masks, where the masks are white on a black background. Then simplifys * the original X-ray to reduce interference from stretchers cradles etc. */ Balance_areas im_in m_control m_list = class Image value { _vislevel = 3; _format = im_in.format; Options = option_1, format < 4 = option_2 { format = im_in.format; option_1 = class { _vislevel = 3; pause = Toggle "Pause Process." true; blur = Slider 0 5 0; _blur = rint blur.value; option = Toggle "Use Scale&Offset Balancing rather than Histogram." true; _option = option; } option_2 = class { _vislevel = 3; pause = Toggle "Pause Process." true; blur = Slider 0 5 0; _blur = rint blur.value; _option = true; } } _control_im = _section_select2 im_in m_control; _control_values = _so_values _control_im; /* blur mask over a set number of pixels then histogram match an area * of the original image defined by m_current to the _control_im * then blend the matched area back into im_in. */ process m_current im_start = im_out { alternative = false; bl_mask = _mask_blur_2 m_current Options._blur; scaled_im = _so_convert _control_values im_start m_current, Options._option == true = _hist_convert_2 _control_im im_start m_current; fmt_im = clip2fmt im_start.format scaled_im; blended_im = im_blend bl_mask scaled_im.value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } value = im_in.value, Options.pause == true = (foldr process im_in m_list).value; }; _so_balance mask im1 im2 gauss = result { /* Extract the undamaged areas. */ im1' = if !mask then Image im1 else 0; im2' = if !mask then Image im2 else 0; /* Find the non_zero means of the undamaged areas. */ m1 = _mean_fn im1'; m2 = _mean_fn im2'; im1_mn = im1' - m1; im2_mn = im2' - m2; scale = (max im1_mn)/(max im2_mn); im2_corrected_a = ((im2 - m2) * scale) + m1; im2_corrected_b = clip2fmt im1'.format im2_corrected_a; /* Option to convert replacement image to scaled gaussian noise */ im2_corrected = im2_corrected_b, gauss == false = _gauss_noise im2_corrected_b; /* Blur mask. */ mask' = _mask_blur_2 mask 5; //mask' = _feather_mask_2 5 mask.value; /* Blend im2 into im1. */ result = im_blend mask' im2_corrected im1; }; /* make a new image of gaussian noise */ _gauss_noise im2 = value { i = Image (im2); width = i.width; height = i.height; mean = Mean i; deviation = Deviation i; noise = im_gaussnoise width height mean deviation; value = clip2fmt i.format noise; }; /* Dilate and blur a mask by a number of pixels. * *_feather_mask_2 pixels mask * = im_convsep (dilate dilate_matrix mask) blur_matrix *{ * dilate_matrix = (iterate (dilate _morph_mask8) _morph_mask8) ? pixels; * blur_matrix = Matrix_con pixels 0 [(map (const 1) [1 .. pixels])]; *}; */ /* Mask is 255 to indicate parts of im1 which are damaged: replace these bits * with the corresponding parts of im2. * * Match the histograms of the two images to hide grey-level differences, be * careful to only consider undamaged sections. * * Feather the edges of the blend to hide the join. * * mask is an Image class. im1, im2 and result are vips images. */ _hist_balance_2 mask im1 im2 = result { format = im_header_int "BandFmt" im1; bands = im_header_int "Bands" im1; /* checks for 8 or 16 bit signed and converts to unsigned */ force_unsigned i = clip2fmt Image_format.UCHAR (i + 128), format == Image_format.CHAR = clip2fmt Image_format.USHORT (i + 32768), format == Image_format.SHORT = i; /* undo any force_unsigned */ format_restore i = clip2fmt Image_format.CHAR (i - 128), format == Image_format.CHAR = clip2fmt Image_format.SHORT (i - 32768), format == Image_format.SHORT = i; /* Find histogram and then zap the zero column (0 == background). For * 16-bit images, the histogram can be smaller than 65535 .... expand * up to full size. */ build_hist i = im_insert big_black h' 0 0 { max_value = Image_format.maxval i.format; h = hist_find i.value; black_1 = image_new 1 1 i.bands Image_format.UINT Image_coding.NOCODING i.type 0 0 0; big_black = image_new max_value 1 i.bands Image_format.UINT Image_coding.NOCODING i.type 0 0 0; h' = im_insert h black_1 0 0; } /* We can't get hists of signed images :-( go unsigned if we have to. */ im1' = force_unsigned im1; im2' = force_unsigned im2; /* Extract the undamaged areas. */ reference = if !mask then Image im1' else 0; secondary = if !mask then Image im2' else 0; /* Find the hists of the undamaged areas. */ h1 = build_hist reference; h2 = build_hist secondary; /* Match greylevels of im2 to match im1. */ im2'' = hist_map (hist_match h1 h2) im2'; /* Feather mask. */ mask' = _mask_blur_2 mask 5; //mask' = _feather_mask_2 5 mask.value; /* Blend im2 into im1. */ im1'' = im_blend mask' im2'' im1'; /* Undo any signed/unsigned nonsense. */ result = format_restore im1''; }; // Blurs the edges of an 8 bit mask, over a given number of pixels. _mask_blur_2 mask pixel = value { pixels = 1, pixel == 0 = pixel; black = im_black (mask.width + (2*pixels)) (mask.height + (2*pixels)) 1; new_mask = Image (im_insert black mask.value pixels pixels); blur_matrix = Matrix_con pixels 0 [(map (const 1) [1 .. pixels])]; im_blur = im_convsep new_mask.value blur_matrix; left = pixels; top = pixels; width = mask.width; height = mask.height; value = im_extract_area im_blur left top width height; }; _so_values im = result { mean_of_im = _mean_fn im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; _so_convert con_values im mask = result { im' = _section_select2 im mask; im_values = _so_values im'; mean_of_con = con_values?0; mean_of_im = im_values?0; max_of_con = con_values?1; max_of_im = im_values?1; scale = (max_of_con)/(max_of_im); im_convert = ((im - mean_of_im) * scale) + mean_of_con; result = clip2fmt im.format im_convert; }; /* Convert the histogram of part of image im_a, depending on the mask im_m, * to match the histogram of im_c. */ _hist_convert_2 control_im im adjust_mask = im_final { max_value = Image_format.maxval im.format; adjust_im = _section_select2 im adjust_mask; hist_c = hist_fn control_im; hist_a = hist_fn adjust_im; /* Find histogram and then edit out any information related to parts * of the image with value 0. Ensure that the histogram represents * the full scale for the appropriate format. Output corrected * histograms for the correct parts of the image. */ hist_fn im_in = output { hist_1 = hist_find im_in; black_1 = clip2fmt hist_1.format (im_black 1 1 1); hist_2 = im_insert hist_1.value black_1 0 0; black_2 = clip2fmt hist_1.format (im_black max_value 1 1); output = im_insert black_2 hist_2 0 0; } im_final = hist_map (hist_match hist_a hist_c) im; }; //------------------------------------------------------------------------------- /*Returns a section of im, defined by a mask, on a black background*/ _section_select2 im_in mask = output { output = clip2fmt im_in.format (im_black im_in.width im_in.height 1), mask == 0 = im_in; }; _mean_fn im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); } ================================================ FILE: share/nip2/compat/7.9/_convert.def ================================================ /* Convert an image ... just set the Type field. */ image_set_type type in = im_copy_set in type (im_header_double "Xres" in) (im_header_double "Yres" in) (im_header_int "Xoffset" in) (im_header_int "Yoffset" in); /* Convert an image ... just set origin */ image_set_origin xoff yoff in = im_copy_set in (im_header_int "Type" in) (im_header_double "Xres" in) (im_header_double "Yres" in) xoff yoff; /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = oo_unary_function to_matrix_op x, is_class x = tom x, is_real x || is_image x = error (errors.badargs ++ "to_matrix") { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (errors.badargs ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i && bands == 1 = (im_vips2mask ((double) i'')).value, is_image i && bands == 3 && width == 1 = error errors.not1band3band { width = im_header_int "Xsize" i; bands = im_header_int "Bands" i; split = bandsplit i; i' = im_insert (split?0) (split?1) 1 0; i'' = im_insert i' (split?2) 2 0; } } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_instanceof "Colour" x = oo_unary_function to_image_op x, is_class x = toi x, is_real x || is_image x = error (errors.badargs ++ "to_image") { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (errors.badargs ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } /* Try to make a real. */ to_real x = to_real x.value, is_class x = x, is_real x = abs x, is_complex x = error (errors.badargs ++ "to_real"); /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error "parse_c: not a digit", ! is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error "parse_number: badly formed number", len parts != 2 = sign * n { parts = splitpl [ member "+-", is_digit ] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, len parts != 4 = (ipart + fpart) * 10 ** exp { err = error "parse_float: badly formed number"; parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10**(len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Print integer as hex. */ print_hex i = "0", chars == [] = "0x" ++ reverse chars { digits = takewhile (not_equal 0) (map (bitwise_and 0xf) (iterate (converse right_shift 4) i)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'a' + (x - 10)); } /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[ 0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [ 0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { // divide by D50 white point xyz' = xyz / Vector [96.4250, 100.0, 82.4680]; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * Vector [95.0470, 100.0, 108.8827]; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { // divide by D65 white point xyz' = xyz / Vector [95.0470, 100.0, 108.8827]; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D50 xyz''' = xyz'' * Vector [96.4250, 100.0, 82.4680]; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz 96.4250 100 82.4680; /* Convert D50 Lab to XYZ. */ im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab 96.4250 100 82.4680; /* ... and mono conversions */ im_sRGB2mono in = clip2fmt (im_header_int "BandFmt" in) (im_recomb in (Matrix [[.3, .6, .1]])); im_mono2sRGB in = (unsigned char) (in ++ in ++ in); /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = fn x, is_image x = error (errors.badargs ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = fn x, is_image x = error (errors.badargs ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (errors.badargs ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (errors.badargs ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, LAB, im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, sRGB, im_mono2sRGB], [B_W, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_mono2sRGB], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy], [XYZ, LAB, im_XYZ2Lab], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS], [XYZ, RGB, im_XYZ2disp], [XYZ, sRGB, im_XYZ2sRGB], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ], [YXY, XYZ, im_Yxy2XYZ], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ], [LAB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ], [LAB, XYZ, im_Lab2XYZ], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ], [LAB, LAB, image_set_type LAB], [LAB, LCH, im_Lab2LCh], [LAB, UCS, im_Lab2UCS], [LAB, RGB, im_Lab2disp], [LAB, sRGB, im_XYZ2sRGB @ im_Lab2XYZ], [LAB, LABQ, im_Lab2LabQ], [LAB, LABS, im_Lab2LabS], [LCH, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LCh2Lab], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab], [LCH, YXY, im_XYZ2Yxy @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LCh2Lab], [LCH, LAB, im_LCh2Lab], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS], [LCH, RGB, im_Lab2disp @ im_LCh2Lab], [LCH, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LCh2Lab], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ], [UCS, XYZ, im_UCS2XYZ], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab], [UCS, LAB, im_UCS2Lab], [UCS, LCH, im_UCS2LCh], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab], [UCS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_UCS2Lab], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_sRGB2XYZ @ im_clip], [LABQ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error ("unable to convert " ++ from_name ++ " to " ++ to_name) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; from_name = Image_type.type_names.lookup 1 0 from; to_name = Image_type.type_names.lookup 1 0 to; } /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_instanceof "Image" x || is_instanceof "Arrow" x || is_instanceof "Colour" x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_instanceof "Image" x = get_type_im x.image.value, is_instanceof "Arrow" x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_instanceof "Colour" x = error ("get_type: unable to get type from " ++ print x) { // get the type from a VIPS image ... but only if it makes sense with // the rest of the image get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.B_W, bands == 1 = type, bands == 3 && is_colorimetric = Image_type.MULTIBAND, bands != 3 && !is_colorimetric = type { is_colorimetric = Image_type.colour_spaces.present 1 type; type = im_header_int "Type" im; coding = im_header_int "Coding" im; bands = im_header_int "Bands" im; } } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; ================================================ FILE: share/nip2/compat/7.9/_errors.def ================================================ /* Lots of error messages. */ errors = class { not1band = "not 1 band image"; not1band8bit = "not 1 band 8-bit image"; not1band3band = "not 1 band image, or 3 band 1 column image"; notodd = "not odd width|height"; notmask = "not mask"; notmaskim = "not image|mask, mask"; badcoding = "not NOCODING or LABPACK"; badlab = "unable to convert to LAB"; allreal = "not all real numbers"; allreg = "not all regions"; badargs = "bad arguments to "; badbands = "images have differing numbers of bands"; badcolour = "bad arg to colourspace function"; badmatrixmatch = "matrix arguments do not match"; badnum = "wrong number of real number arguments"; bandFmt = "bad BandFmt"; internal = "menu macro sanity failure!"; lengthdiff = "list arguments differ in length"; noimage = "no image argument"; noregion = "no region argument"; notim = "not image"; notimcmplx = "not image|complex"; notimnotreal = "non image arguments not all real numbers"; notimreg = "not image|region"; notregimreg = "not region, image|region"; notimregnum = "not image|region|number"; notimregstr = "not image|region|string"; notmatnum = "not real|matrix|mask"; notmatrix = "not matrix|mask"; notodd_square_matrix = "not odd-sided square matrix|mask"; notreal = "not real number"; notreglist = "not region|[region]"; notsquare_matrix = "not square matrix|mask"; regwrong = "regions not on two images"; sfacgt1 = "shrink factors should be >= 1"; unknownType = "unknown type"; usage = "usage: "; }; ================================================ FILE: share/nip2/compat/7.9/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = (unsigned int) (h ++ v) { h = (x - 1) * im_fgrey x y; v = (y - 1) * im_rot90 (im_fgrey y x); } /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12 */ image_new w h b fmt coding type pixel xoff yoff = im'''' { im = im_black w h b + pixel; im' = clip2fmt fmt im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type im''; im'''' = image_set_origin xoff yoff im'''; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l (size / 2) (size / 2); A1 = im_fgrey size size; /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = L ++ A2 ++ A4; } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (map (const (max_value.lookup 1 0 format)) [1 .. bands]) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } ================================================ FILE: share/nip2/compat/7.9/_list.def ================================================ /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn (tl l), fn (hd l) = l; /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* foldl fn st l: fold list l up from the left using function fn and start value st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st (hd l)) (tl l); /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn (hd l) (tl l); /* foldr fn st l: fold up list l, right to left, with function fn and start * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn (hd l) (foldr fn st (tl l)); /* foldrl fn l: like foldr, but use the 1st element as the start value * * foldr1 fn [1,2,3,4] == (2 fn (3 fn (4 fn 1))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = foldr fn (hd l) (tl l); /* Search a list for an element, returning it's index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn (hd l) = search (tl l) (n + 1); } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = hd l : init (tl l); /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* land l: and all the elements of list l together * * land (map (==0) list) == true, if every element of list is zero. * land :: [bool] -> bool */ land = foldr logical_and true; /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = hd l, tl l == [] = last (tl l); /* len l: length of list l * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a = l?0; b = l?1; x = tl (tl l); } /* lor l: or all the elements of list l together * * lor (map (equal 0) list) == true, if any element of list is zero. * lor :: [bool] -> bool */ lor = foldr logical_or false; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map fn' (zip2 l1 l2) { fn' p = fn p?0 p?1; } /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map fn' (zip3 l1 l2 l3) { fn' p = fn p?0 p?1 p?2; } /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = lor (map (equal x) l); /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a = hd l; x = tl l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scan fn st l: apply (fold fn r) to every initial segment of a list * * scan add 0 [1,2,3] == [1,3,6] * scan :: (* -> ** -> *) -> * -> [**] -> [*] */ scan fn = g { g st l = [st], l == [] = st : g (fn st (hd l)) (tl l); } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a = hd l1; x = tl l1; b = hd l2; y = tl l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by fn * * split is_space "hello world" == ["hello", "world"] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/7.9/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true of character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && land (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, land (map is_obj l) = true, land (map is_list l) && land (map (not @ is_obj) l) && land (map is_rectangular l) && len l > 0 && land (map (equal (hd lengths)) (tl lengths)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; lengths = map len l; } /* is_matrix l: is l a list of lists of real numbers, all the same length */ is_matrix l = is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [] = is_matrix l && len l == len (hd l); /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [] = is_matrix l && (len l) % 2 == 1 && (len (l?0)) % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && (len l) % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_Guide x = is_instanceof "HGuide" x || is_instanceof "VGuide" x; is_Point x = is_instanceof "Point" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && land (map xy l) { xy l = is_real_list l && len l == 2; } ================================================ FILE: share/nip2/compat/7.9/_stdenv.def ================================================ /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; join a b = a ++ b; subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; if_then_else a b c = if a then b else c; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error "unimplemented vector operation" { zeros = map (const 0) [1 .. len vec]; ones = map (const 1) [1 .. len vec]; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } /* Macbeth chart patch names. */ _macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (errors.badargs ++ "bandsplit") { bands = im_header_int "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = Image (concat (map get_value l)), is_listof (is_instanceof "Image") l = concat l, is_listof is_image l = error (errors.badargs ++ "bandjoin") { get_value x = x.value; } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = error (errors.badargs ++ "mean") { mean_op = Operator "mean" mean_object Operator_type.COMPOUND false; mean_object x = im_avg x, is_image x = mean_list x, is_real_list x || is_matrix x = error (errors.badargs ++ "mean"); mean_list l = s / n { totals = sum l; n = totals?0; s = totals?1; } // return [n, sum] for a list of numbers, or a list of list of num // etc. sum x = foldr accumulate [0, 0] x { accumulate x sofar = [n + 1, x + s], is_real x = [n + n', s + s'], is_list x = error "mean_list: not real or [real]" { n = sofar?0; s = sofar?1; sub_acc = sum x; n' = sub_acc?0; s' = sub_acc?1; } } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = error (errors.badargs ++ "deviation") { deviation_op = Operator "deviation" deviation_object Operator_type.COMPOUND false; deviation_object x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (errors.badargs ++ "deviation"); deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { totals = sum_sum2_list l; n = totals?0; s = totals?1; s2 = totals?2; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { n = sofar?0; s = sofar?1; s2 = sofar?2; sub_acc = sum_sum2_list x; n' = sub_acc?0; s' = sub_acc?1; s2' = sub_acc?2; } } } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = error (errors.badargs ++ "abs") { abs_op = Operator "abs" abs_object Operator_type.COMPOUND false; abs_object x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (errors.badargs ++ "abs"); abs_list l = (foldr1 add (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = error (errors.badargs ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec_object Operator_type.COMPOUND false; abs_vec_object x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (Vector (map abs_vec_list x)), is_matrix x = error (errors.badargs ++ "abs_vec"); abs_vec_list l = (foldr1 add (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (foldr1 add (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_matrix x, is_list x && is_list (hd x) = error (errors.badargs ++ "transpose") { transpose_op = Operator "transpose" transpose_object Operator_type.COMPOUND_REWRAP false; transpose_object x = transpose_matrix x, is_matrix x = transpose_image x, is_image x = error (errors.badargs ++ "transpose"); transpose_matrix l = [], l' == [] = (map hd l') : (transpose_matrix (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (errors.badargs ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (errors.badargs ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } rot90 x = oo_unary_function rot90_op x, is_class x = im_rot90 x, is_image x = error (errors.badargs ++ "rot90") { rot90_op = Operator "rot90" rot90_object Operator_type.COMPOUND_REWRAP false; rot90_object x = rot90_matrix x, is_matrix x = im_rot90 x, is_image x = error (errors.badargs ++ "rot90"); // slow, but what the heck rot90_matrix l = (im_rotate_dmask90 (Matrix l)).value; } rot180 x = oo_unary_function rot180_op x, is_class x = im_rot180 x, is_image x = error (errors.badargs ++ "rot180") { rot180_op = Operator "rot180" rot180_object Operator_type.COMPOUND_REWRAP false; rot180_object x = rot180_matrix x, is_matrix x = im_rot180 x, is_image x = error (errors.badargs ++ "rot180"); // slow, but what the heck rot180_matrix l = (im_rotate_dmask90 (im_rotate_dmask90 (Matrix l))).value; } rot270 x = oo_unary_function rot270_op x, is_class x = im_rot270 x, is_image x = error (errors.badargs ++ "rot270") { rot270_op = Operator "rot270" rot270_object Operator_type.COMPOUND_REWRAP false; rot270_object x = rot270_matrix x, is_matrix x = im_rot270 x, is_image x = error (errors.badargs ++ "rot270"); // slow, but what the heck rot270_matrix l = (im_rotate_dmask90 (im_rotate_dmask90 (im_rotate_dmask90 (Matrix l)))).value; } rotquad x = oo_unary_function rotquad_op x, is_class x = im_rotquad x, is_image x = error (errors.badargs ++ "rotquad") { rotquad_op = Operator "rotquad" rotquad_object Operator_type.COMPOUND_REWRAP false; rotquad_object x = rotquad_matrix x, is_matrix x = im_rotquad x, is_image x = error (errors.badargs ++ "rotquad"); rotquad_matrix l = error "rotquad matrix: not implemented"; } flipud x = oo_unary_function flipud_op x, is_class x = im_flipver x, is_image x = error (errors.badargs ++ "flipud") { flipud_op = Operator "flipud" flipud_object Operator_type.COMPOUND_REWRAP false; flipud_object x = flipud_matrix x, is_matrix x = im_flipver x, is_image x = error (errors.badargs ++ "flipud"); flipud_matrix l = reverse l; } fliplr x = oo_unary_function fliplr_op x, is_class x = im_fliphor x, is_image x = error (errors.badargs ++ "fliplr") { fliplr_op = Operator "fliplr" fliplr_object Operator_type.COMPOUND_REWRAP false; fliplr_object x = fliplr_matrix x, is_matrix x = im_fliphor x, is_image x = error (errors.badargs ++ "fliplr"); fliplr_matrix l = map reverse l; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = x, is_number x = error (errors.badargs ++ "max") { max_op = Operator "max" max_list Operator_type.COMPOUND false; max_list x = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_matrix x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = x, is_number x = error (errors.badargs ++ "min") { min_op = Operator "min" min_list Operator_type.COMPOUND false; min_list x = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_matrix x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = error (errors.badargs ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos_object Operator_type.COMPOUND false; maxpos_object x = maxpos_matrix x, is_matrix x = im_maxpos x, is_image x = error (errors.badargs ++ "maxpos"); maxpos_matrix m = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = error (errors.badargs ++ "minpos") { minpos_op = Operator "minpos" minpos_object Operator_type.COMPOUND false; minpos_object x = minpos_matrix x, is_matrix x = im_minpos x, is_image x = error (errors.badargs ++ "minpos"); minpos_matrix m = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = error (errors.badargs ++ "stats") { stats_op = Operator "stats" stats_object Operator_type.COMPOUND false; stats_object x = im_stats (to_image x).value, is_matrix x = im_stats x, is_image x = error (errors.badargs ++ "stats"); } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (errors.badargs ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = rint_value x, is_image x || is_number x = error (errors.badargs ++ "rint") { rint_op = Operator "rint" rint_value Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = scale_prim x { scale_op = Operator "scale" scale_prim Operator_type.COMPOUND_REWRAP false; scale_prim x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (errors.badargs ++ "scale"); scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (errors.badargs ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (errors.badargs ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (errors.badargs ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = im_falsecolour x, is_image x = error (errors.badargs ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (errors.badargs ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (errors.badargs ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } recomb matrix image = colour_unary recomb_op image { recomb_op x = im_recomb x matrix, is_image x = error (errors.badargs ++ "recomb"); } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = extract_area_prim obj { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" extract_area_prim Operator_type.COMPOUND_REWRAP false; extract_area_prim obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (errors.badargs ++ "extract_area"); extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_row_prim obj { y' = to_real y; extract_row_op = Operator "extract_row" extract_row_prim Operator_type.COMPOUND_REWRAP false; extract_row_prim obj = im_extract_area obj 0 y' width 1, is_image obj = [obj?y'], is_matrix obj = error (errors.badargs ++ "extract_row") { width = im_header_int "Xsize" obj; } } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_column_prim obj { x' = to_real x; extract_column_op = Operator "extract_column" extract_column_prim Operator_type.COMPOUND_REWRAP false; extract_column_prim obj = im_extract_area obj x' 0 1 height, is_image obj = map (converse cons [] @ converse subscript x') obj, is_matrix obj = error (errors.badargs ++ "extract_column") { height = im_header_int "Ysize" obj; } } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_lr_prim a b { join_lr_op = Operator "join_lr" join_lr_prim Operator_type.COMPOUND_REWRAP false; join_lr_prim a b = im_extract_area (im_insert a b a_width 0) 0 0 (a_width + b_width) out_height, is_image a && is_image b = map2 join a b, is_matrix a && is_matrix b = error (errors.badargs ++ "join_lr") { a_height = im_header_int "Ysize" a; b_height = im_header_int "Ysize" b; a_width = im_header_int "Xsize" a; b_width = im_header_int "Xsize" b; out_height = min_pair a_height b_height; } } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_tb_prim a b { join_tb_op = Operator "join_tb" join_tb_prim Operator_type.COMPOUND_REWRAP false; join_tb_prim a b = im_extract_area (im_insert a b 0 a_height) 0 0 out_width (a_height + b_height), is_image a && is_image b = map (take out_matrix_width) a ++ map (take out_matrix_width) b, is_matrix a && is_matrix b = error (errors.badargs ++ "join_tb") { a_height = im_header_int "Ysize" a; b_height = im_header_int "Ysize" b; a_width = im_header_int "Xsize" a; b_width = im_header_int "Xsize" b; out_width = min_pair a_width b_width; out_matrix_width = min_pair a_matrix_width b_matrix_width { a_matrix_width = len a?0; b_matrix_width = len b?0; } } } insert x y small big = oo_binary_function insert_op small big, is_class small = oo_binary'_function insert_op small big, is_class big = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (errors.badargs ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } measure x y w h u v image = oo_unary_function measure_op image, is_class image = im_measure image (to_real x) (to_real y) (to_real w) (to_real h) (to_real u) (to_real v), is_image image = error (errors.badargs ++ "measure") { measure_op = Operator "measure" (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; } rotate angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_similarity image (cos angle) (sin angle) 0 0, is_real angle && is_image image = error (errors.badargs ++ "rotate") { rotate_op = Operator "rotate" rotate Operator_type.COMPOUND_REWRAP false; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = im_header_int "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function (clip2fmt_op format) image, is_class image = im_clip2fmt image format, is_image image = error (errors.badargs ++ "clip2fmt") { clip2fmt_op format = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { image = (unsigned char) im_mask2vips (Matrix m2); m2_width = im_header_int "Xsize" image; m2_height = im_header_int "Ysize" image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = im_embed image 0 (m1.width - 1) (m1.height - 1) (m2_width + 2 * (m1.width - 1)) (m2_height + 2 * (m1.height - 1)); // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image mask, is_image image = error (errors.badargs ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image mask, is_image image = error (errors.badargs ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image mask, is_image image = error (errors.badargs ++ "conv") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image w h n, is_image image = error (errors.badargs ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } hist_find image = oo_unary_function hist_find_op image, is_class image = im_histgr image (-1), is_image image = error (errors.badargs ++ "hist_find") { hist_find_op = Operator "hist_find" hist_find Operator_type.COMPOUND_REWRAP false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image bins, is_image image = error (errors.badargs ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" hist_find_nD Operator_type.COMPOUND_REWRAP false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (errors.badargs ++ "hist_map") { hist_map_op = Operator "hist_map" hist_map Operator_type.COMPOUND_REWRAP false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (errors.badargs ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (errors.badargs ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (errors.badargs ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = im_lhisteq image w h, is_image image = error (errors.badargs ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; } resize xfac yfac interp image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (errors.badargs ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac yfac, is_int xfac && is_int yfac && xfac >= 1 && yfac >= 1 && interp == Interpolate.nearest_neighbour // downscale by integer factor, nearest neighbour = im_subsample im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && interp == Interpolate.nearest_neighbour // upscale by any factor, nearest neighbour // can't really do this right ... upscale by integer part, then // bilinear to exact size = scale (break xfac)?1 (break yfac)?1 (im_zoom im (break xfac)?0 (break yfac)?0), xfac >= 1 && yfac >= 1 && interp == Interpolate.nearest_neighbour // downscale by any factor, nearest neighbour // can't really do this right ... downscale by integer part, // then bilinear to exact size = scale (1 / (break xfac')?1) (1 / (break yfac')?1) (im_subsample im (break xfac')?0 (break yfac')?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.nearest_neighbour // upscale by any factor, bilinear = scale xfac yfac im, xfac >= 1 && yfac >= 1 && interp == Interpolate.bilinear // downscale by any factor, bilinear // block shrink by integer factor, then bilinear resample to // exact = scale (1 / (break xfac')?1) (1 / (break yfac')?1) (im_shrink im (break xfac')?0 (break yfac')?0), xfac' >= 1 && yfac' >= 1 && interp == Interpolate.bilinear = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac ++ "\n" ++ " yfac = " ++ print yfac ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate.names.lookup 1 0 interp ++ ")") { xfac' = 1 / xfac; yfac' = 1 / yfac; // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // binlinear resize scale xfac yfac im = im_affine im xfac 0 0 yfac 0 0 0 0 (width * xfac) (height * yfac) { width = im_header_int "Xsize" im; height = im_header_int "Ysize" im; } } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map3 (map_trinary fn) a b c, is_list a && is_list b && is_list c = map2 (map_trinary fn a) b c, is_list b && is_list c = map2 (map_trinary (converse31 fn) b) a c, is_list a && is_list c = map2 (map_trinary (converse32 fn) c) a b, is_list a && is_list b = map (map_trinary fn a b) c, is_list c = map (map_trinary (converse32 fn) a c) b, is_list b = map (map_trinary (converse34 fn) b c) a, is_list a = fn a b c { converse31 fn a b c = fn b a c; converse32 fn a b c = fn c a b; converse33 fn a b c = fn a c b; converse34 fn a b c = fn b c a; } /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map2 (map_binary fn) a b, is_list a && is_list b = map (map_binary fn a) b, is_list b = map (map_binary (converse fn) b) a, is_list a = fn a b; /* Map a 1-ary function over an object. */ map_unary fn a = map (map_unary fn) a, is_list a = fn a; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop block_size overlap i = map chop' [0, step .. height] { width = im_header_int "Xsize" i; height = im_header_int "Ysize" i; bands = im_header_int "Bands" i; format = im_header_int "BandFmt" i; type = im_header_int "Type" i; /* Unique pixels per tile. */ step = block_size - overlap; /* Calculate padding ... pad up to block_size pixel boundary. */ sx = block_size + (width - width % step); sy = block_size + (height - height % step); /* Expand image with black to pad size. */ background = image_new sx sy bands format Image_coding.NOCODING type 0 0 0; pad = im_insert background i 0 0; /* Chop up a row. */ chop' y = map chop'' [0, step .. width] { chop'' x = im_extract_area pad x y block_size block_size; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = vjoin (map hjoin il) { /* Join a list of tiles horizontally. */ hjoin l = foldl1 lrj l { lrj l r = im_lrmerge l r (hoverlap - im_header_int "Xsize" l) 0 10; } /* Join a list of tiles vertically. */ vjoin l = foldl1 tbj l { tbj t b = im_tbmerge t b 0 (voverlap - im_header_int "Ysize" t) 10; } } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } ================================================ FILE: share/nip2/compat/7.9/_types.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), "any"]; check_bool = [is_bool, "boolean"]; check_real = [is_real, "real"]; check_ureal = [is_ureal, "unsigned real"]; check_preal = [is_preal, "positive real"]; check_real_list = [is_real_list, "list of real"]; check_string = [is_string, "string"]; check_string_list = [is_string_list, "list of string"]; check_int = [is_int, "integer"]; check_uint = [is_uint, "unsigned integer"]; check_pint = [is_pint, "positive integer"]; check_matrix = [is_matrix, "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, "0, 1, 2 or 3"]; check_image = [is_image, "image"]; check_xy_list = [is_xy_list, "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_instanceof "Matrix_base", "Matrix"]; check_colour_space = [is_colour_space, "colour_space"]; check_rectangular = [is_rectangular, "rectangular [[*]]"]; check_Guide = [is_Guide, "HGuide or VGuide"]; check_Point = check_instance "Point"; check_Colour = check_instance "Colour"; /* Check a set of args. Grab _check_table. It's a list of two check * lists: the first checks each arg, and the second checks all args * together. * * - each line in argcheck is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in allcheck is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down a bit. */ check_args x = x, badargs == [] && badalls == [] = error message { argcheck = x._check_args; allcheck = x._check_all; // join two strings up with a separator string join_sep j a b = a ++ j ++ b; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = errors.badargs ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\nusage\n" ++ indent ++ usage ++ "\nwhere\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ "you passed " ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ "you passed\n" ++ concat (map fmt_arg argcheck) { fmt n = "condition failed: " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make usage note usage = x.name ++ " " ++ foldr (join_sep " ") [] (map (extract 1) argcheck); // make arg type notes arg_types = foldr (join_sep "\n") [] (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "and\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = foldr (join_sep "\n") [] all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error ("unknown binary operator: " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error ("unknown unary operator: " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = error ("No method found for binary operator.\n" ++ "left = " ++ print a ++ "\n" ++ "operator = " ++ op.op_name ++ "\n" ++ "right = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = error ("No method found for binary operator.\n" ++ "left = " ++ print a ++ "\n" ++ "operator = " ++ op.op_name ++ "\n" ++ "right = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error ("No method found for unary operator.\n" ++ "operator = " ++ op.op_name ++ "\n" ++ "argument = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; /* Default: no checks ... override in subclasses. */ _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; /* Provide a fallback for class == thing ... just use pointer * equality. */ oo_binary_table op x = [ [ pointer_equal this x, op.op_name == "equal" || op.op_name == "equal'" ], [ not_pointer_equal this x, op.op_name == "not_equal" || op.op_name == "not_equal'" ] ]; oo_unary_table op = []; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ] ++ super._check_args; // methods oo_binary_table op x = [ [ this.Real (op.fn this.value x.value), is_instanceof "Real" x && op.type == Operator_type.ARITHMETIC ], [ this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ], [ op.fn this.value x.value, is_instanceof "Real" x && op.type == Operator_type.RELATIONAL ], [ op.fn this.value x, !is_class x ] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [ this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC ], [ op.fn this.value, true ] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ] ++ super._check_args; // methods oo_binary_table op x = [ [ if value then x?0 else x?1, op.op_name == "if_then_else" ], [ this.Bool (op.fn this.value x.value), is_instanceof "Bool" x ], [ this.Bool (op.fn this.value x), is_bool x ] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [ this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL ] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ] ++ super._check_args; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ] ++ super._check_args; Real value = Number caption value; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ] ++ super._check_args; } // the old name Filename = Pathname "Pick a file"; /* Vector type ... just a finite list of real ... handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ] ++ super._check_args; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [ this.Vector (op.fn this.value x.value), is_instanceof "Vector" x && (op.op_name == "join" || op.op_name == "join'") ], // extra check for lengths equal [ this.Vector (map_binary op.fn this.value x.value), is_instanceof "Vector" x && len value == len x.value && op.type == Operator_type.ARITHMETIC ], [ this.Vector (map_binary op.fn this.value x.value), is_instanceof "Real" x && op.type == Operator_type.ARITHMETIC ], [ this.Vector (map_binary op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ], // need extra length check [ this.Vector (map bool_to_real (map_binary op.fn this.value x.value)), is_instanceof "Vector" x && len value == len x.value && op.type == Operator_type.RELATIONAL ], [ this.Vector (map bool_to_real (map_binary op.fn this.value x.value)), is_instanceof "Real" x && op.type == Operator_type.RELATIONAL ], [ this.Vector (map bool_to_real (map_binary op.fn this.value x)), is_real x && op.type == Operator_type.RELATIONAL ], [ this.Vector (op.fn this.value x.value), is_instanceof "Vector" x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP ], [ x.Image (vec op'.op_name x.value value), is_instanceof "Image" x ], [ vec op'.op_name x value, is_image x ], [ op.fn this.value x, is_real x ] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [ this.Vector (map_unary op.fn this.value), op.type == Operator_type.ARITHMETIC ], [ this.Vector (map bool_to_real (map_unary op.fn this.value)), op.type == Operator_type.RELATIONAL ], [ this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP ], [ op.fn this.value, true ] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ] ++ super._check_args; // calculate these from value width = len value?0; height = len value; // methods oo_binary_table op x = [ // mat multiply is special [ this.Matrix_base mul.value, is_instanceof "Matrix_base" x && op.op_name == "multiply" ], [ this.Matrix_base mul'.value, is_instanceof "Matrix_base" x && op.op_name == "multiply'" ], // mat divide is also special [ this.Matrix_base div.value, is_instanceof "Matrix_base" x && op.op_name == "divide" ], [ this.Matrix_base div'.value, is_instanceof "Matrix_base" x && op.op_name == "divide'" ], // power -1 means invert [ this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power" ], [ this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power" ], [ error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'" ], // matrix op vector ... treat a vector as a 1 row matrix [ this.Matrix_base (map (map_binary op'.fn x.value) this.value), is_instanceof "Vector" x && op.type == Operator_type.ARITHMETIC ], [ this.Matrix_base (map_binary op.fn this.value x.value), (is_instanceof "Matrix_base" x || is_instanceof "Real" x) && op.type == Operator_type.ARITHMETIC ], [ this.Matrix_base (map_binary op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ], // compound ... don't do iteration [ this.Matrix_base (op.fn this.value x.value), (is_instanceof "Matrix_base" x || is_instanceof "Real" x || is_instanceof "Vector" x) && op.type == Operator_type.COMPOUND_REWRAP ] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [ this.Matrix_base (map_unary op.fn this.value), op.type == Operator_type.ARITHMETIC ], [ this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP ], [ op.fn this.value, true ] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ] ++ super._check_args; Matrix_base value = Matrix_vips value scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {}; /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = im_read_dmask (expand filename); /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ] ++ super._check_args; _check_all = [ [len value == 3, "len value == 3"] ] ++ super._check_all; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = im_header_int "Type" im; bands = im_header_int "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [ itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP ] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [ itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP ] ] ++ super.oo_unary_table op; Vector value = Colour colour_space value; } /* Base slider type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ] ++ super._check_args; _check_all = [ [from < to, "from < to"] ] ++ super._check_all; // methods oo_binary_table op x = [ [ this.Scale (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_instanceof "Scale" x && op.type == Operator_type.ARITHMETIC ], [ this.Scale (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC ] ] ++ super.oo_binary_table op x; Real value = Scale from to value; } /* Base slider type. */ Slider f t v = Scale "" f t v; /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ] ++ super._check_args; Bool value = Toggle caption value; } /* Base option type. */ Option caption labels value = class Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ] ++ super._check_args; } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ] ++ super._check_args; /* present col x: is there an x in column col */ present col x = member (map (extract col) value) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error ("item " ++ print x ++ " not in table") { n = index (equal x) (map (extract from) value); } } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ] ++ super._check_args; // derived right = left + width; bottom = top + height; // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // equal to another rect equal r = left == r.left && top == r.top && width == r.width && height == r.height; // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); // operator overloading // just define equal and not equal oo_binary_table op x = [ [ equal x, is_instanceof "Rect" x && (op.op_name == "equal" || op.op_name == "equal'") ], [ !equal x, is_instanceof "Rect" x && (op.op_name == "not_equal" || op.op_name == "not_equal'") ] ] ++ super.oo_binary_table op x; } /* Values for Compression field in image. */ Image_compression = class { NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NOCODING = 0; COLQUANT = 1; LABPACK = 2; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647 // INT ] ? fmt, fmt >= 0 && fmt <= INT = error errors.bandFmt; } /* Type field. */ Image_type = class { FOURIER = 24; YXY = 23; sRGB = 22; LABS = 21; LCH = 19; UCS = 18; RGB = 17; LABQ = 16; CMYK = 15; CMC = 14; LAB = 13; XYZ = 12; LUT = 11; HISTOGRAM = 10; POWER_SPECTRUM = 9; BLUE_ONLY = 8; GREEN_ONLY = 7; RED_ONLY = 6; YUV = 5; IR = 4; XRAY = 3; LUMINACE = 2; B_W = 1; MULTIBAND = 0; /* Table to get names <-> numbers. */ type_names = Table [ [ "FOURIER", FOURIER ], [ "YXY", YXY ], [ "sRGB", sRGB ], [ "LABS", LABS ], [ "LCH", LCH ], [ "UCS", UCS ], [ "RGB", RGB ], [ "LABQ", LABQ ], [ "CMYK", CMYK ], [ "CMC", CMC ], [ "LAB", LAB ], [ "XYZ", XYZ ], [ "LUT", LUT ], [ "HISTOGRAM", HISTOGRAM ], [ "POWER_SPECTRUM", POWER_SPECTRUM ], [ "BLUE_ONLY", BLUE_ONLY ], [ "GREEN_ONLY", GREEN_ONLY ], [ "RED_ONLY", RED_ONLY ], [ "YUV", YUV ], [ "IR", IR ], [ "XRAY", XRAY ], [ "LUMINACE", LUMINACE ], [ "B_W", B_W ], [ "MULTIBAND", MULTIBAND ] ]; /* Table relating nip's colour space names and VIPS's Type numbers. */ colour_spaces = Table [ [ "XYZ", Image_type.XYZ ], [ "Yxy", Image_type.YXY ], [ "Lab", Image_type.LAB ], [ "LCh", Image_type.LCH ], [ "UCS", Image_type.UCS ], [ "RGB", Image_type.RGB ], [ "sRGB", Image_type.sRGB ] ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ] ++ super._check_args; // fields from VIPS header width = im_header_int "Xsize" value; height = im_header_int "Ysize" value; bands = im_header_int "Bands" value; format = im_header_int "BandFmt" value; coding = im_header_int "Coding" value; type = im_header_int "Type" value; xres = im_header_double "Xres" value; yres = im_header_double "Yres" value; xoffset = im_header_int "Xoffset" value; yoffset = im_header_int "Yoffset" value; filename = im_header_string "filename" value; // convenience ... the area our pixels occupy as a rect rect = Rect (-xoffset) (-yoffset) width height; // extract an area, addressed in nip cordinates extract_area left top width height = im_extract_area value (left + xoffset) (top + yoffset) width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ [ this.Image (op.fn this.value x.value), is_instanceof "Image" x || is_instanceof "Real" x ], [ this.Image result_image, op.op_name == "if_then_else" ], [ this.Image (vec op.op_name value x.value), is_instanceof "Vector" x ], [ this.Image (op.fn this.value x), is_number x || is_image x ] ] ++ super.oo_binary_table op x { to_image x = x, is_image x = x.value, is_instanceof "Image" x = black + x { black = im_black width height target_bands; } // get a member from the first of a list of objects to have it get_property has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } // get things about our output from inputs in this order objects = [then_part, else_part, this]; then_part = x?0; else_part = x?1; // properties of our output image target_bands = get_property (has_member "bands") (get_member "bands") objects; target_format = get_property (has_member "format") (get_member "format") objects; target_type = get_property has_type get_type objects; then_image = to_image then_part; else_image = to_image else_part; then_image' = clip2fmt target_format then_image; else_image' = clip2fmt target_format else_image; result_image = image_set_type target_type (if value then then_image' else else_image'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [ this.Image result, is_image result ], [ result, true ] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file str = class Image value { _check_args = [ [str, "str", check_string] ] ++ super._check_args; file = Filename str; value = vips_image file.value; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ] ++ super._check_args; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = image.extract_area left top width height, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = Area image left top width height; } Arrow image left top width height = class Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ] ++ super._check_args; _check_all = [ [image.rect.includes_rect this, "image.rect.includes_rect this"] ] ++ super._check_all; // our rect, translated to image cods (ie. offset by origin) image_rect = Rect (left + image.xoffset) (top + image.yoffset) width height; // methods oo_binary_table op x = [ [ this.Arrow this.image left' top' width' height', is_instanceof "Arrow" x && op.type == Operator_type.ARITHMETIC ] ] ++ super.oo_binary_table op x { left' = op.fn this.left x.left; top' = op.fn this.top x.top; width' = op.fn this.width x.width; height' = op.fn this.height x.height; } oo_unary_search op = [ [ this.Arrow this.image left' top' width' height', op.type == Operator_type.ARITHMETIC ] ] ++ super.oo_unary_table op { left' = op.fn this.left; top' = op.fn this.top; width' = op.fn this.width; height' = op.fn this.height; } } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = VGuide image left; } Point image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = Point image left top; } // Mark is the name nip2 expects for Point Mark = Point; // convenience functions: make relative on an image ... subtract origin // offset, ie. make in pixel coordinates Region_relative image u v w h = Region image (image.width * u - image.xoffset) (image.height * v - image.yoffset) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u - image.xoffset) (image.height * v - image.yoffset) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u - image.xoffset) (image.height * v - image.yoffset) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v - image.yoffset); HGuide_relative image u = HGuide image (image.width * u - image.xoffset); Point_relative image u v = Point image (image.width * u - image.xoffset) (image.height * v - image.yoffset); Interpolate = class { nearest_neighbour = 0; bilinear = 1; bicubic = 2; /* Table to map interpol numbers to descriptive strings */ names = Table [ [ "Nearest neighbour", nearest_neighbour ], [ "Bilinear", bilinear ], [ "Bicubic", bicubic ] ]; } Separator = class {} // renamed in 7.9 estpar = im_estpar; resample = im_transform_search; ================================================ FILE: share/nip2/compat/8.2/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = print_profile, has_type image && get_type image == Image_type.CMYK && has_bands image && get_bands image >= 4 = monitor_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/8.2/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 2; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 20; fs = Scale "Sharpen flat areas by" 0 5 0.5; js = Scale "Sharpen jaggy areas by" 0 5 1; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_hough_item = class Menupullright "_Hough Transform" "transform to parameter space" { Line_item = class Menuaction "_Line" "find straight line Hough transform" { action a = class _result { _vislevel = 3; pspace_width = Expression "Parameter space width" 64; pspace_height = Expression "Parameter space height" 64; _result = map_unary line a { line a = hough_line (to_real pspace_width) (to_real pspace_height) a; } } } Circle_item = class Menuaction "_Circle" "find circle Hough transform" { action a = class _result { _vislevel = 3; scale = Expression "Scale down parameter space by" 10; min_radius = Expression "Minimum radius" 10; max_radius = Expression "Maximum radius" 30; _result = map_unary circle a { circle a = hough_circle (to_real scale) (to_real min_radius) (to_real max_radius) a; } } } } Filter_coordinate_item = class Menupullright "_Coordinate Transform" "various coordinate transforms" { // run a function which wants a complex arg on a non-complex two-band // image run_cmplx fn x = re x' ++ im x' { x' = fn (x?0, x?1); } Polar_item = class Menuaction "_Polar" "transform to polar coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_polar a { to_polar im = mapim interp.value map' im { // xy image, origin in the centre, scaled to fit image to // a circle xy = make_xy im.width im.height; xy' = xy - Vector [im.width / 2, im.height / 2]; scale = min [im.width, im.height] / im.width; xy'' = 2 * xy' / scale; // to polar, scale vertical axis to 360 degrees map = run_cmplx polar xy''; map' = map * Vector [1, im.height / 360]; } } } } Rectangular_item = class Menuaction "_Rectangular" "transform to rectangular coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_rect a { to_rect im = mapim interp.value map'' im { // xy image, vertical scaled to 360 degrees xy = make_xy im.width im.height; xy' = xy * Vector [1, 360 / im.height]; // to rect, scale to image rect map = run_cmplx rectangular xy'; scale = min [im.width, im.height] / im.width; map' = map * scale / 2; map'' = map' + Vector [im.width / 2, im.height / 2]; } } } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } ================================================ FILE: share/nip2/compat/8.2/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Interpolate_bilinear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; caption = Expression "Chart caption" "none"; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; xcaption = Expression "X axis caption" "none"; ycaption = Expression "Y axis caption" "none"; series_captions = Expression "Series captions" ["Band 0"]; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range ++ captions; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; captions = concat (map test caption_options) ++ [$series_captions => series_captions.expr] { caption_options = [ $caption => caption.expr, $xcaption => xcaption.expr, $ycaption => ycaption.expr ]; test x = [], value == "none" = [option_name => value] { [option_name, value] = x; } } image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/8.2/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp h v image, aspect = resize interp fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize interp fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_map_item = class Menuaction "_Map" "map an image through a 2D transform image" { action a b = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_binary trans a b { trans a b = mapim interp.value in index { // get the index image first [index, in] = sortc (const is_twocomponent) [a, b]; // is a two-component image, ie. one band complex, or // two-band non-complex is_twocomponent x = is_nonc x || is_c x; is_nonc x = has_bands x && get_bands x == 2 && has_format x && !is_complex_format (get_format x); is_c x = has_bands x && get_bands x == 1 && has_format x && is_complex_format (get_format x); is_complex_format f = f == Image_format.COMPLEX || f == Image_format.DPCOMPLEX; } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 100; sy = Expression "Start y" 100; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/8.2/Magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate Magick.def 13-Apr-2014 snibgo Put "new image" items into sub-menu. New class VirtualPixlBack. 17-Apr-2014 snibgo Many small changes. A few new menu options. Created sub-menu for multi-input operations. 3-May-2014 jcupitt Put quotes around ( in shadow to help unix Last update: 17-Apr-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ // We don't need Noop. /*=== Magick_noop_item = class Menuaction "_Noop" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_testPar_item = class Menuaction "_TestPar" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "( +clone ) +append ", "\"%s\"" ]; _result = Magick.system command x; } } /* Removed Read_item and Write_item, much better to use nip2 load/save image. * Plus they can load all libMagick formats anyway. */ // Put "new image" items into sub-menu Magick_NewImageMenu_item = class Menupullright "_New image" "make a new image" { Magick_newcanvas_item = class Menuaction "_Solid colour" "make image of solid colour" { action = class _result { _vislevel = 3; size = Magick.Size_widget; colour = Magick.generalcol_widget; command = Magick.command [ size._flag, "\"canvas:" ++ colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_builtin_item = class Menuaction "_Built-in image" "create a built-in image" { action = class _result { _vislevel = 3; builtin = Magick.builtin_widget; command = Magick.command [ builtin._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_gradient_item = class Menuaction "_Gradient" "make a linear gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; topColour = Magick.generalcol_widget; bottomColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"gradient:", topColour._flag, "-", bottomColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } Magick_hald_item = class Menuaction "_Hald-clut image" "create an identity hald-clut image" { action = class _result { _vislevel = 3; order = Expression "order" 8; command = Magick.command [ "hald:" ++ print order.expr, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_pattern_item = class Menuaction "_Pattern" "create pattern" { action = class _result { _vislevel = 3; size = Magick.Size_widget; pattern = Magick.pattern_widget; command = Magick.command [ size._flag, pattern._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_plasma_item = class Menuaction "_Plasma image" "create plasma image" { action = class _result { _vislevel = 3; size = Magick.Size_widget; // FIXME? ColourA-ColourB. // FIXME? Allow plasma:fractal? command = Magick.command [ size._flag, "plasma:", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_radialgradient_item = class Menuaction "_Radial gradient" "make a radial gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; innerColour = Magick.generalcol_widget; outerColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"radial-gradient:", innerColour._flag, "-", outerColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } } // end Magick_NewImageMenu_item Magick_MultiMenu_item = class Menupullright "_Multiple inputs" "make an image from multiple images" { Magick_composite_item = class Menuaction "_Composite" "composite two images (without mask)" { action x y = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_compositeMask_item = class Menuaction "_Composite masked" "composite two images (with mask)" { action x y z = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system3 command x y z; } } // FIXME: other operations like remap that take another image as arguments are: // mask (pointless?), texture, tile (pointless?) // FIXME: operations that take a filename that isn't an image: // cdl, profile Magick_clut_item = class Menuaction "_Clut" "replace values using second image as colour look-up table" { action x y = class _result { _vislevel = 3; // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" command = Magick.command [ "\"%s\"", "\"%s\"", "-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_haldclut_item = class Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"", "-hald-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } // Encipher and decipher: key files can be text or image files. Magick_encipher_item = class Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { action x = class _result { _vislevel = 3; pathname = Pathname "Read key file" ""; isDecipher = Toggle "Decipher" false; command = Magick.command [ "\"%s\"", if pathname.value == "" then "" else ( ( if isDecipher then "-decipher " else "-encipher ") ++ ( "\"" ++ pathname.value ++ "\"" ) ), "\"%s\"" ]; _result = Magick.system command x; } } Magick_profile_item = class Menuaction "_Profile" "assigns/applies an ICC profile" { action x = class _result { _vislevel = 3; pathname1 = Pathname "Read profile file" ""; pathname2 = Pathname "Read profile file" ""; command = Magick.command [ "\"%s\"", if pathname1.value == "" then "" else ( "-profile " ++ "\"" ++ pathname1.value ++ "\""), if pathname2.value == "" then "" else ( "-profile " ++ "\"" ++ pathname2.value ++ "\""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_remap_item = class Menuaction "_Remap" "reduce colours to those in another image" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-remap", "\"%s\"", "\"%s\"" ]; _result = Magick.system2 command x y; } } } // end Magick_MultiMenu_item Magick_image_type_item = class Menuaction "_Image Type" "change image type" { action x = class _result { _vislevel = 3; imagetype = Magick.imagetype_widget; command = Magick.command [ "\"%s\"", imagetype._flag, "\"%s\"" ]; _result = Magick.system command x; } } sep2 = Menuseparator; Magick_alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = Magick.alpha_widget; command = Magick.command [ "\"%s\"", alpha._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = Magick.text_widget; font = Magick.Font_widget; geometry = Magick.AnnotGeometry_widget; gravity = Magick.gravity_widget; foreground = Magick.foreground_widget; undercol = Magick.undercol_widget; antialias = Magick.antialias_widget; command = Magick.command [ "\"%s\"", font._flag, antialias._flag, gravity._flag, foreground._flag, undercol._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoGamma_item = class Menuaction "_AutoGamma" "automatic gamma" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-gamma", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoLevel_item = class Menuaction "_AutoLevel" "automatic level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-level", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blurSharpMenu_item = class Menupullright "_Blur/Sharpen" "blur and sharpen" { Magick_adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; // note: adaptive-blur doesn't regard VP. command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-adaptive-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_blur_item = class Menuaction "_Blur" "blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gaussianBlur_item = class Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-gaussian-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_motionBlur_item = class Menuaction "_Motion Blur" "simulate motion blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; channels = Magick.ch_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-motion-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotationalBlur_item = class Menuaction "_RotationalBlur" "blur around the centre" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; angle = Scale "angle (degrees)" (-360) 360 20; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-radial-blur", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_selectiveBlur_item = class Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; threshold = Scale "Threshold (percent)" 0 100 50; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", intensity._flag, virtpixback._flag, channels._flag, "-selective-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print threshold.value ++ "%%", "\"%s\"" ]; _result = Magick.system command x; } } sep1 = Menuseparator; Magick_adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-adaptive-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sharpen_item = class Menuaction "_Sharpen" "sharpen" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_unsharpen_item = class Menuaction "_Unsharp" "sharpen with unsharp mask" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; gain = Scale "Gain" (-10) 10 1; threshold = Scale "Threshold" 0 1 0.05; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-unsharp", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print gain.value ++ "+" ++ print threshold.value, "\"%s\"" ]; _result = Magick.system command x; } } } // end BlurSharpMenu_item Magick_border_item = class Menuaction "_Border" "add border of given colour" { action x = class _result { _vislevel = 3; compose = Magick.compose_widget; width = Expression "Width" 3; bordercol = Magick.bordercol_widget; command = Magick.command [ "\"%s\"", compose._flag, bordercol._flag, "-border", print width.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_brightCont_item = class Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { action x = class _result { _vislevel = 3; bri = Scale "brightness" (-100) 100 0; con = Scale "contrast" (-100) 100 0; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-brightness-contrast", print bri.value ++ "x" ++ print con.value, "\"%s\"" ]; _result = Magick.system command x; } } // Note: canny requires ImageMagick 6.8.9-0 or later. Magick_canny_item = class Menuaction "_Canny" "detect a wide range of edges" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; lowPc = Scale "lower percent" 0 100 10; highPc = Scale "lower percent" 0 100 10; command = Magick.command [ "\"%s\"", "-canny", concat ["\"", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print lowPc.value ++ "%%+" ++ print highPc.value ++ "%%" ++ "\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_charcoal_item = class Menuaction "_Charcoal" "simulate a charcoal drawing" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; factor = Scale "factor" 0 50 1; command = Magick.command [ "\"%s\"", intensity._flag, "-charcoal", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_chop_item = class Menuaction "_Chop" "remove pixels from the interior" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-chop", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorize_item = class Menuaction "_Colorize" "colorize by given amount" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; val = Scale "value" 0 100 100; command = Magick.command [ "\"%s\"", foreground._flag, "-colorize", print val.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colors_item = class Menuaction "_Colors" "reduce number of colors" { action x = class _result { _vislevel = 3; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; quantize = Magick.colorspace_widget; colors = Expression "Colours" 3; command = Magick.command [ "\"%s\"", "-quantize", quantize._flag, "-treedepth", print treedepth.expr, dither._flag, "-colors", print colors.expr, "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: color-matrix? Magick_colorspace_item = class Menuaction "_Colourspace" "convert to arbitrary colourspace" { action x = class _result { _vislevel = 3; colsp = Magick.colorspace_widget; command = Magick.command [ "\"%s\"", "-colorspace", colsp._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorspaceGray_item = class Menuaction "_Colourspace gray" "convert to gray using given intensity method" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-colorspace gray", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrast_item = class Menuaction "_Contrast" "increase or reduce the contrast" { action x = class _result { _vislevel = 3; isReduce = Toggle "reduce contrast" false; command = Magick.command [ "\"%s\"", (if isReduce then "+" else "-") ++ "contrast", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrastStretch_item = class Menuaction "_Contrast stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-contrast-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: convolve (bias, kernel) Magick_crop_item = class Menuaction "_Crop" "cut out a rectangular region" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-crop", geometry._flag, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_deskew_item = class Menuaction "_Deskew" "straighten the image" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; // FIXME: toggle auto-crop? command = Magick.command [ "\"%s\"", "-deskew", "\"" ++ print threshold.value ++ "%%\"", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_despeckle_item = class Menuaction "_Despeckle" "reduce the speckles" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-despeckle", "\"%s\"" ]; _result = Magick.system command x; } } Magick_distort_item = class Menuaction "_Distort" "distort using a method and arguments" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; distort = Magick.distort_widget; args = String "Arguments" "1,0"; isPlus = Toggle "Extend to show entire image" false; command = Magick.command [ "\"%s\"", virtpixback._flag, (if isPlus then "+" else "-") ++ "distort", distort._flag, args.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_draw_item = class Menuaction "_Draw" "annotate with one or more graphic primitives" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; command = Magick.command [ "\"%s\"", foreground._flag, "-draw", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_edge_item = class Menuaction "_Edge" "detect edges" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-edge", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_emboss_item = class Menuaction "_Emboss" "emboss" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-emboss", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_enhance_item = class Menuaction "_Enhance" "enhance a noisy image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-enhance", "\"%s\"" ]; _result = Magick.system command x; } } Magick_equalize_item = class Menuaction "_Equalize" "equalize the histogram" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-equalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_evaluate_item = class Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; operation = Magick.evaluate_widget; val = Expression "value" 5; isPc = Toggle "Value is percent" false; command = Magick.command [ "\"%s\"", channels._flag, operation._flag, print val.expr ++ if isPc then "%%" else "", "\"%s\"" ]; _result = Magick.system command x; } } Magick_extent_item = class Menuaction "_Extent" "set the image size and offset" { action x = class _result { _vislevel = 3; background = Magick.background_widget; compose = Magick.compose_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, background._flag, gravity._flag, "-extent", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_FlipFlopMenu_item = class Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { Magick_flip_item = class Menuaction "_Flip vertically" "mirror upside-down" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flip", "\"%s\"" ]; _result = Magick.system command x; } } Magick_flop_item = class Menuaction "_Flop horizontally" "mirror left-right" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flop", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transpose_item = class Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transpose +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transverse_item = class Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transverse +repage", "\"%s\"" ]; _result = Magick.system command x; } } } // end Magick_FlipFlopMenu_item Magick_floodfill_item = class Menuaction "_Floodfill" "recolour neighbours that match" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; fuzz = Magick.fuzz_widget; coordinate = Magick.coordinate_widget; // -draw "color x,y floodfill" command = Magick.command [ "\"%s\"", foreground._flag, "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-draw \" color", coordinate._flag, "floodfill \"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_frame_item = class Menuaction "_Frame" "surround with border or beveled frame" { action x = class _result { _vislevel = 3; border = Magick.bordercol_widget; compose = Magick.compose_widget; matte = Magick.mattecol_widget; geometry = Magick.FrameGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, border._flag, matte._flag, "-frame", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_function_item = class Menuaction "_Function" "evaluate a function on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; function = Magick.function_widget; // FIXME: explain values; use sensible defaults. values = String "values" "0,0,0,0"; command = Magick.command [ "\"%s\"", channels._flag, "-function", function._flag, values.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_fx_item = class Menuaction "_Fx" "apply a mathematical expression" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; interpolate = Magick.interpolate_widget; virtpixback = Magick.VirtualPixelBack_widget; args = String "Expression" "u*1/2"; command = Magick.command [ "\"%s\"", channels._flag, interpolate._flag, virtpixback._flag, "-fx", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_gamma_item = class Menuaction "_Gamma" "apply a gamma correction" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; gamma = Magick.gamma_widget; command = Magick.command [ "\"%s\"", channels._flag, "-gamma", print gamma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradient_item = class Menuaction "_Gradient" "apply a linear gradient" { action x = class _result { _vislevel = 3; colourA = Magick.generalcol_widget; colourB = Magick.generalcol_widget; position = Option "colourA is at" [ "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"] 0; _baryArg = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 = "dunno"; command = Magick.command [ "\"%s\"", "-sparse-color barycentric \"" ++ _baryArg ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradientCorn_item = class Menuaction "_Gradient corners" "apply a bilinear gradient between the corners" { action x = class _result { _vislevel = 3; colour_top_left = Magick.generalcol_widget; colour_top_right = Magick.generalcol_widget; colour_bottom_left = Magick.generalcol_widget; colour_bottom_right = Magick.generalcol_widget; command = Magick.command [ "\"%s\"", "-sparse-color bilinear \"" ++ "0,0," ++ colour_top_left._flag ++ ",%%[fx:w-1],0" ++ colour_top_right._flag ++ ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", "+depth", "\"%s\"" ]; _result = Magick.system command x; } } Magick_histogram_item = class Menuaction "_Histogram" "make a histogram image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-define histogram:unique-colors=false histogram:" ++ "\"%s\"" ]; _result = Magick.system command x; } } Magick_implode_item = class Menuaction "_Implode" "implode pixels about the center" { action x = class _result { _vislevel = 3; factor = Scale "factor" 0 20 1; interpolate = Magick.interpolate_widget; // FIXME: virtual-pixel? command = Magick.command [ "\"%s\"", interpolate._flag, "-implode", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_level_item = class Menuaction "_Level" "adjust the level of channels" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "black point" (-100) 200 0; wht = Scale "white point" (-100) 200 100; gam = Scale "gamma" 0 30 1; isPc = Toggle "Levels are percent" true; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level", "\"" ++ print blk.value ++ "," ++ print wht.value ++ (if isPc then "%%" else "") ++ "," ++ print gam.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_levelCols_item = class Menuaction "_Level colors" "adjust levels to given colours" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; colour_black = Magick.generalcol_widget; colour_white = Magick.generalcol_widget; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level-colors", "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_linearStretch_item = class Menuaction "_Linear stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", channels._flag, "-linear-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_magnify_item = class Menuaction "_Magnify" "double the size of the image with pixel art scaling" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-magnify", "\"%s\"" ]; _result = Magick.system command x; } } Magick_modulate_item = class Menuaction "_Modulate" "modulate brightness, saturation and hue" { action x = class _result { _vislevel = 3; modcolsp = Magick.ModColSp_widget; bright = Scale "brightness" 0 200 100; sat = Scale "saturation" 0 200 100; hue = Scale "hue" 0 200 100; command = Magick.command [ "\"%s\"", modcolsp._flag, "-modulate", print bright.value ++ "," ++ print sat.value ++ "," ++ print hue.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_monochrome_item = class Menuaction "_Monochrome" "transform to black and white" { action x = class _result { _vislevel = 3; // FIXME: also intensity? intensity = Magick.intensity_widget; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; command = Magick.command [ "\"%s\"", intensity._flag, dither._flag, "-treedepth", print treedepth.expr, "-monochrome", "\"%s\"" ]; _result = Magick.system command x; } } Magick_morphology_item = class // See http://www.imagemagick.org/Usage/morphology/ Menuaction "_Morphology" "apply a morphological method" { action x = class _result { _vislevel = 3; method = Magick.morphmeth_widget; iter = Expression "Iterations (-1=repeat until done)" 1; kernel = Magick.kernel_widget; // FIXME: custom kernel eg "3x1+2+0:1,0,0" // width x height + offsx + offsy : {w*h values} // each value is 0.0 to 1.0 or "NaN" or "-" // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 // but // ring takes: radius1, radius2, scale // rectangle and comet take: width x height + offsx + offsy // blur takes: radius x sigma // FIXME: for now, simply allow any string input. kernel_arg = String "Kernel arguments" ""; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-morphology", method._flag ++ ":" ++ print iter.expr, kernel._flag ++ (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_negate_item = class Menuaction "_Negate" "negate" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-negate", "\"%s\"" ]; _result = Magick.system command x; } } Magick_addNoise_item = class Menuaction "_add Noise" "add noise" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; attenuate = Scale "attenuate" 0 1.0 1.0; noise = Magick.noise_widget; command = Magick.command [ "\"%s\"", channels._flag, "-attenuate", print attenuate.value, noise._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_normalize_item = class Menuaction "_Normalize" "normalize" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-normalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_opaque_item = class Menuaction "_Opaque" "change this colour to the fill colour" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; fill = Magick.foreground_widget; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", fill._flag, channels._flag, "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "opaque", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_paint_item = class Menuaction "_Paint" "simulate an oil painting" { action x = class _result { _vislevel = 3; rad = Expression "radius" 10; command = Magick.command [ "\"%s\"", "-paint", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } /*=== FIXME Bug; remove for now. Polaroid_item = class Menuaction "_Polaroid" "simulate a polaroid picture" { action x = class _result { _vislevel = 3; angle = Scale "angle" (-90) 90 20; command = Magick.command [ "\"%s\"", "-polaroid", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_posterize_item = class Menuaction "_Posterize" "reduce to (n) levels per channel" { action x = class _result { _vislevel = 3; levels = Expression "levels" 3; command = Magick.command [ "\"%s\"", "-posterize", print levels.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_raise_item = class Menuaction "_Raise" "lighten or darken image edges" { action x = class _result { _vislevel = 3; thk = Expression "Thickness" 3; command = Magick.command [ "\"%s\"", "-raise", print thk.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_resize_item = class Menuaction "_Resize" "resize to given width and height" { action x = class _result { _vislevel = 3; filter = Magick.filter_widget; type = Magick.ResizeType_widget; width = Scale "Width" 1 100 10; height = Scale "Height" 1 100 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", filter._flag, "-" ++ type._flag, "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "!") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_roll_item = class Menuaction "_Roll" "roll an image horizontally or vertically" { action x = class _result { _vislevel = 3; rollx = Expression "X" 3; rolly = Expression "Y" 3; command = Magick.command [ "\"%s\"", "-roll", (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotate_item = class Menuaction "_Rotate" "rotate" { action x = class _result { _vislevel = 3; angle = Scale "angle (degrees)" (-360) 360 20; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, "+distort", "SRT", print angle.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -segment, but cluster-threshold should be percentage of image area. Magick_sepia_item = class Menuaction "_Sepia tone" "simulate a sepia-toned photo" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; command = Magick.command [ "\"%s\"", "-sepia-tone", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shade_item = class Menuaction "_Shade" "shade with a distant light source" { action x = class _result { _vislevel = 3; azimuth = Scale "Azimuth (degrees)" (-360) 360 0; elevation = Scale "Elevation (degrees)" 0 90 45; command = Magick.command [ "\"%s\"", "-shade", print azimuth.value ++ "x" ++ print elevation.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_shadow_item = class Menuaction "_Shadow" "simulate a shadow" { action x = class _result { _vislevel = 3; shadowCol = Magick.generalcol_widget; opacity = Scale "Opacity (percent)" 0 100 75; sigma = Scale "Sigma" 0 30 2; // FIXME: make offsets a single widget? offsx = Scale "X-offset" (-20) 20 4; offsy = Scale "Y-offset" (-20) 20 4; arePc = Toggle "offsets are percentages" false; // FIXME: raw operation creates page offset, which vips dislikes. // So we take this futher, compositing with source. command = Magick.command [ "\"%s\"", "\"(\" +clone", "-background", "\"" ++ shadowCol._flag ++ "\"", "-shadow", concat [ "\"", print opacity.value, "x", print sigma.value, (if offsx.value >= 0 then "+" else ""), print offsx.value, (if offsy.value >= 0 then "+" else ""), print offsy.value, (if arePc then "%%" else ""), "\"" ], "\")\" +swap -background None -layers merge", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shave_item = class Menuaction "_Shave" "shave pixels from the edges" { action x = class _result { _vislevel = 3; width = Scale "Width" 0 50 10; height = Scale "Height" 0 50 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", "-shave", "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shear_item = class Menuaction "_Shear" "shear along the x-axis and/or y-axis" { action x = class _result { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-shear", print shearX.expr ++ "x" ++ print shearY.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sigmoid_item = class Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; contrast = Scale "contrast" 0 30 3; midpoint = Scale "mid-point (percent)" 0 100 50; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "sigmoidal-contrast", "\"" ++ print contrast.value ++ "x" ++ print midpoint.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_sketch_item = class Menuaction "_Sketch" "simulate a pencil sketch" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; command = Magick.command [ "\"%s\"", "-sketch", print radius.value ++ "x" ++ print sigma.value ++ (if angle >= 0 then ("+" ++ print angle.value) else ""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_solarize_item = class Menuaction "_Solarize" "negate all pixels above a threshold level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-solarize", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -sparse-color needs abitrary list of {x,y,colour}. Magick_splice_item = class Menuaction "_Splice" "splice a colour into the image" { action x = class _result { _vislevel = 3; background = Magick.background_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", background._flag, gravity._flag, "-splice", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_spread_item = class Menuaction "_Spread" "displace pixels by random amount" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; amount = Expression "Amount (pixels)" 5; command = Magick.command [ "\"%s\"", virtpixback._flag, "-spread", print amount.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_statistic_item = class Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { action x = class _result { _vislevel = 3; width = Expression "Width" 5; height = Expression "Height" 5; statisticType = Magick.StatType_widget; command = Magick.command [ "\"%s\"", "-statistic", statisticType._flag, print width.expr ++ "x" ++ print height.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = Magick.angle_widget; command = Magick.command [ "\"%s\"", "-swirl", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_thresholdMenu_item = class Menupullright "_Threshold" "make black or white" { Magick_threshold_item = class Menuaction "_Threshold" "apply black/white threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blackThreshold_item = class Menuaction "_Black threshold" "where below threshold set to black" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-black-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_whiteThreshold_item = class Menuaction "_White threshold" "where above threshold set to white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-white-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_latThreshold_item = class Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { action x = class _result { _vislevel = 3; width = Expression "Width" 10; height = Expression "Height" 10; offset = Scale "Offset (percent)" (-100) 100 0; // note: "-lat" doesn't respond to channels command = Magick.command [ "\"%s\"", "-lat", concat ["\"", print width.expr, "x", print height.expr, (if offset.value >= 0 then "+" else ""), print offset.value, "%%\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_randThreshold_item = class Menuaction "_Random Threshold" "between specified limits, apply random threshold" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; low = Scale "Low threshold" 0 100 10; high = Scale "High threshold" 0 100 90; isPc = Toggle "Thresholds are percent" true; command = Magick.command [ "\"%s\"", channels._flag, "-random-threshold", "\"" ++ print low.value ++ "x" ++ print high.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } } // end ThresholdMenu_item // Note: alternatives include: // convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif Magick_tile_item = class Menuaction "_Tile" "fill given size with tiled image" { action x = class _result { _vislevel = 3; size = Magick.Size_widget; command = Magick.command [ size._flag, "tile:" ++ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_tint_item = class Menuaction "_Tint" "apply a tint" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; amount = Scale "amount (percent)" 0 100 50; command = Magick.command [ "\"%s\"", foreground._flag, "-tint", // snibgo note: although the amount is a percentage, it doesn't need "%" character. print amount.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_transparent_item = class Menuaction "_Transparent" "make this colour transparent" { action x = class _result { _vislevel = 3; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "transparent", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_trim_item = class Menuaction "_Trim" "trims away border" { action x = class _result { _vislevel = 3; fuzz = Magick.fuzz_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-trim +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_uniqueCols_item = class Menuaction "_Unique colours" "discard all but one of any pixel color" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-unique-colors", "\"%s\"" ]; _result = Magick.system command x; } } Magick_vignette_item = class Menuaction "_Vignette" "soften the edges in vignette style" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; rx = Scale "Rolloff x (percent)" 0 100 10; ry = Scale "Rolloff y (percent)" 0 100 10; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-vignette", print radius.value ++ "x" ++ print sigma.value ++ (if rx.value >= 0 then "+" else "") ++ print rx.value ++ (if ry.value >= 0 then "+" else "") ++ print ry.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_wave_item = class Menuaction "_Wave" "shear the columns into a sine wave" { action x = class _result { _vislevel = 3; amplitude = Scale "Amplitude (pixels)" 0 100 10; wavelength = Scale "Wavelength (pixels)" 0 100 10; command = Magick.command [ "\"%s\"", "-wave", print amplitude.value ++ "x" ++ print wavelength.value, "\"%s\"" ]; _result = Magick.system command x; } } ================================================ FILE: share/nip2/compat/8.2/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/8.2 start_DATA = \ Colour.def \ _convert.def \ Filter.def \ _generate.def \ Histogram.def \ Image.def \ _joe_extra.def \ _joe_utilities.def \ _list.def \ _magick.def \ Magick.def \ Math.def \ Matrix.def \ _Object.def \ Object.def \ _predicate.def \ Preferences.ws \ _stdenv.def \ Tasks.def \ _types.def \ Widgets.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/8.2/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/8.2/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/8.2/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/8.2/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/8.2/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Interpolate_bilinear xfactor yfactor scale_im; _offset_im = resize Interpolate_bilinear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/8.2/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/8.2/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/8.2/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart > 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; ================================================ FILE: share/nip2/compat/8.2/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/compat/8.2/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Interpolate_bilinear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Interpolate_bilinear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Interpolate_bilinear f1 1 b1 {b1 = resize Interpolate_bilinear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/8.2/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/8.2/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/8.2/_magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate _magick.def Add 0-ary and 2-ary system Put utility funcs into a Magick class 11-Apr-2014 snibgo Added VirtualPixelBack for cases where background is only relevant when VP=Background 17-Apr-2014 snibgo Many small changes. 2-May-2014 jcupitt Added Magick.version 30-June-2014 Put single-quotes around command exe to help win 1-July-2014 Automatically fall back to gm if we can't find convert 17-July-2014 better GM support Last update: 17-July-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ /* Put these in a class to avoid filling the main namespace with IM stuff. */ Magick = class { // turn $PATH into [["comp1", "comp2"], ["comp1", "comp2"] ..] system_search_path = map path_parse (split (equal path_sep) (expand "$PATH")) { path_sep = ':', expand "$SEP" == "/" = ';'; } // the first name[.exe] on $PATH, or "" search_for name = hits?0, hits != [] = "" { exe_name = name ++ expand "$EXEEXT"; form_path p = path_absolute (p ++ [exe_name]); paths = map form_path system_search_path; hits = dropwhile (equal []) (map search paths); } // first gm on path, or "" gm_path = search_for "gm"; // first convert on $PATH, or "" // we check for the convert we ship first convert_path = vips_convert, vips_convert != "" = search_for "convert" { // the convert we ship with the vips binary on some platforms, or "" vips_convert = search (path_absolute convert) { vipshome = path_parse (expand "$VIPSHOME"); convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; } } use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; // Are we in GM or IM mode? use_gm = true, use_gm_pref && gm_path != "" = false, !use_gm_pref && convert_path != "" = false, convert_path != "" = true, gm_path != "" = error "neither IM nor GM executable found"; command_path = gm_path, use_gm = convert_path; // try to get the version as eg. [6, 7, 7, 10] // GM versions are smaller, typically [1, 3, 18] version = map parse_int (split (member ".-") version_string) { [output] = vips_call "system" ["'" ++ command_path ++ "' -version"] [$log=>true]; version_string = (split (equal ' ') output)?1, use_gm = (split (equal ' ') output)?2; } // make a command-line ... args is a [str] we join with spaces command args = "'" ++ command_path ++ "' " ++ join_sep " " args' { args' = ["convert"] ++ args, use_gm = args; } // capabilities ... different versions support different features, we // turn features on and off based on these // would probably be better to test for caps somehow has_intensity = false, use_gm = version?0 > 6 || version?1 > 7; has_channel = false, use_gm = version?0 > 6 || version?1 > 7; system0 cmd = system_image0 cmd; system cmd x = map_unary (system_image cmd) x; system2 cmd x y = map_binary (system_image2 cmd) x y; system3 cmd x y z = map_trinary (system_image3 cmd) x y z; radius_widget = Scale "Radius" 0 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle (degrees)" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; gamma_widget = Scale "Gamma" 0 10 1; colors_widget = Scale "Colors" 1 10 3; resize_widget = Scale "Resize (percent)" 0 500 100; fuzz_widget = Scale "Fuzz (percent)" 0 100 0; blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; // a colour with no enclosing quotes ... use this if we know there are // some quotes at an outer level print_colour_nq triple = concat ["#", concat (map fmt triple)] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } // we need the quotes because # is the comment character in *nix print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; GeneralCol triple = class Colour "sRGB" triple { _flag = print_colour_nq triple; Colour_edit space triple = this.GeneralCol triple; } generalcol_widget = GeneralCol [0, 0, 0]; Background triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" false; _flag = "-background " ++ if isNone then "None" else print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Bordercol triple = class Colour "sRGB" triple { _flag = "-bordercolor " ++ print_colour triple; Colour_edit space triple = this.Bordercol triple; } bordercol_widget = Bordercol [0, 0, 0]; Mattecol triple = class Colour "sRGB" triple { _flag = "-mattecolor " ++ print_colour triple; Colour_edit space triple = this.Mattecol triple; } mattecol_widget = Mattecol [189, 189, 189]; // FIXME: Undercolour, like many others, can have alpha channel. // How does user input this? With a slider? Undercol triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" true; _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); Colour_edit space triple = this.Undercol triple; } undercol_widget = Undercol [0, 0, 0]; changeCol_widget = class { _vislevel = 3; colour = GeneralCol [0, 0, 0]; fuzz = fuzz_widget; nonMatch = Toggle "change non-matching colours" false; } Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Remove", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Builtin builtin = class Option_string "Builtin" [ // See http://www.imagemagick.org/script/formats.php "rose:", "logo:", "wizard:", "granite:", "netscape:" ] builtin { _flag = builtin; Option_edit caption labels value = this.Builtin labels?value; } builtin_widget = Builtin "rose:"; channels_widget = class { // FIXME? Can we grey-out alpha when we have no alpha channel, // show CMY(K) instead of RGB(K) etc? // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. ChanR valueR = class Toggle "Red" valueR { _flag = "R", valueR = ""; Toggle_edit caption valueR = this.ChanR valueR; } channelR = ChanR true; ChanG valueG = class Toggle "Green" valueG { _flag = "G", valueG = ""; Toggle_edit caption valueG = this.ChanG valueG; } channelG = ChanG true; ChanB valueB = class Toggle "Blue" valueB { _flag = "B", valueB = ""; Toggle_edit caption valueB = this.ChanB valueB; } channelB = ChanB true; ChanK valueK = class Toggle "Black" valueK { _flag = "K", valueK = ""; Toggle_edit caption valueK = this.ChanK valueK; } channelK = ChanK true; ChanA valueA = class Toggle "Alpha" valueA { _flag = "A", valueA = ""; Toggle_edit caption valueA = this.ChanA valueA; } channelA = ChanA false; ChanSy valueSy = class Toggle "Sync" valueSy { _flag = ",sync", valueSy = ""; Toggle_edit caption valueSy = this.ChanSy valueSy; } channelSy = ChanSy true; _rgbka = concat [channelR._flag, channelG._flag, channelB._flag, channelK._flag, channelA._flag ]; _flag = "", _rgbka == "" || !has_channel = concat [ "-channel ", _rgbka, channelSy._flag ]; } ch_widget = channels_widget; Colorspace colsp = class Option_string "Colorspace" [ "CIELab", "CMY", "CMYK", "Gray", "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "Lab", "LCH", "LCHab", "LCHuv", "LMS", "Log", "Luv", "OHTA", "Rec601Luma", "Rec601YCbCr", "Rec709Luma", "Rec709YCbCr", "RGB", "scRGB", "sRGB", "Transparent", "XYZ", "YCbCr", "YDbDr", "YCC", "YIQ", "YPbPr", "YUV" ] colsp { _flag = colsp; Option_edit caption labels value = this.Colorspace labels?value; } colorspace_widget = Colorspace "sRGB"; Compose comp = class Option_string "Compose method" [ "Atop", "Blend", "Blur", "Bumpmap", "ChangeMask", "Clear", "ColorBurn", "ColorDodge", "Colorize", "CopyBlack", "CopyBlue", "CopyCyan", "CopyGreen", "Copy", "CopyMagenta", "CopyOpacity", "CopyRed", "CopyYellow", "Darken", "DarkenIntensity", "DivideDst", "DivideSrc", "Dst", "Difference", "Displace", "Dissolve", "Distort", "DstAtop", "DstIn", "DstOut", "DstOver", "Exclusion", "HardLight", "Hue", "In", "Lighten", "LightenIntensity", "LinearBurn", "LinearDodge", "LinearLight", "Luminize", "Mathematics", "MinusDst", "MinusSrc", "Modulate", "ModulusAdd", "ModulusSubtract", "Multiply", "None", "Out", "Overlay", "Over", "PegtopLight", "PinLight", "Plus", "Replace", "Saturate", "Screen", "SoftLight", "Src", "SrcAtop", "SrcIn", "SrcOut", "SrcOver", "VividLight", "Xor" ] comp { _flag = "-compose " ++ comp; Option_edit caption labels value = this.Compose labels?value; } compose_widget = Compose "Over"; // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack coordinate_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; _flag = concat [print x.expr, ",", print y.expr]; }; Distort distort = class Option_string "Distort" [ "Affine", "AffineProjection", "ScaleRotateTranslate", "SRT", "Perspective", "PerspectiveProjection", "BilinearForward", "BilinearReverse", "Polynomial", "Arc", "Polar", "DePolar", "Barrel", "BarrelInverse", "Shepards", "Resize" ] distort { _flag = distort; Option_edit caption labels value = this.Distort labels?value; } distort_widget = Distort "SRT"; Dither dither = class Option_string "Dither" [ "None", "FloydSteinberg", "Riemersma" ] dither { _flag = "-dither " ++ dither; Option_edit caption labels value = this.Dither labels?value; } dither_widget = Dither "FloydSteinberg"; Evaluate eval = class Option_string "Evaluate operation" [ "Abs", "Add", "AddModulus", "And", "Cos", "Cosine", "Divide", "Exp", "Exponential", "GaussianNoise", "ImpulseNoise", "LaplacianNoise", "LeftShift", "Log", "Max", "Mean", "Median", "Min", "MultiplicativeNoise", "Multiply", "Or", "PoissonNoise", "Pow", "RightShift", "Set", "Sin", "Sine", "Subtract", "Sum", "Threshold", "ThresholdBlack", "ThresholdWhite", "UniformNoise", "Xor" ] eval { _flag = "-evaluate " ++ eval; Option_edit caption labels value = this.Evaluate labels?value; } evaluate_widget = Evaluate "Add"; Filter filt = class Option_string "Filter" [ "default", "Bartlett", "Blackman", "Bohman", "Box", "Catrom", "Cosine", "Cubic", "Gaussian", "Hamming", "Hann", "Hermite", "Jinc", "Kaiser", "Lagrange", "Lanczos", "Lanczos2", "Lanczos2Sharp", "LanczosRadius", "LanczosSharp", "Mitchell", "Parzen", "Point", "Quadratic", "Robidoux", "RobidouxSharp", "Sinc", "SincFast", "Spline", "Triangle", "Welch" ] filt { _flag = if filt == "default" then "" else "-filter " ++ filt; Option_edit caption labels value = this.Filter labels?value; } filter_widget = Filter "default"; Function func = class Option_string "Function" [ "Polynomial", "Sinusoid", "Arcsin", "Arctan" ] func { _flag = func; Option_edit caption labels value = this.Function labels?value; } function_widget = Function "Polynomial"; // "Polynomial (a[n], a[n-1], ... a[1], a[0])", // "Sinusoid (freq, phase, amp, bias)", // "Arcsin (width, centre, range, bias)", // "Arctan (slope, centre, range, bias)" Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; ImageType imagetype = class Option_string "Image type" [ "Bilevel", "ColorSeparation", "ColorSeparationAlpha", "ColorSeparationMatte", "Grayscale", "GrayscaleAlpha", "GrayscaleMatte", "Optimize", "Palette", "PaletteBilevelAlpha", "PaletteBilevelMatte", "PaletteAlpha", "PaletteMatte", "TrueColorAlpha", "TrueColorMatte", "TrueColor" ] imagetype { _flag = "-type " ++ imagetype; Option_edit caption labels value = this.ImageType labels?value; } imagetype_widget = ImageType "TrueColor"; Intensity intensity = class Option_string "Intensity (gray conversion)" [ "Average", "Brightness", "Lightness", "MS", "Rec601Luma", "Rec601Luminance", "Rec709Luma", "Rec709Luminance", "RMS" ] intensity { _flag = "-intensity " ++ intensity, has_intensity = ""; Option_edit caption labels value = this.Intensity labels?value; } intensity_widget = Intensity "Rec709Luminance"; Interpolate interp = class Option_string "Interpolate" [ "default", "Average", "Average4", "Average9", "Average16", "Background", "Bilinear", "Blend", "Integer", "Mesh", "Nearest", "NearestNeighbor", "Spline" ] interp { _flag = if interp == "default" then "" else "-interpolate " ++ interp; Option_edit caption labels value = this.Interpolate labels?value; } interpolate_widget = Interpolate "default"; Kernel kernel = class Option_string "Kernel" [ "Unity", "Gaussian", "DoG", "LoG", "Blur", "Comet", "Binomial", "Laplacian", "Sobel", "FreiChen", "Roberts", "Prewitt", "Compass", "Kirsch", "Diamond", "Square", "Rectangle", "Disk", "Octagon", "Plus", "Cross", "Ring", "Peaks", "Edges", "Corners", "Diagonals", "LineEnds", "LineJunctions", "Ridges", "ConvexHull", "ThinSe", "Skeleton", "Chebyshev", "Manhattan", "Octagonal", "Euclidean" // FIXME: custom kernel ] kernel { _flag = kernel; Option_edit caption labels value = this.Kernel labels?value; } kernel_widget = Kernel "Unity"; ModColSp msp = class Option_string "modulate colorspace" [ "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "LCH" ] msp { _flag = "-set option:modulate:colorspace " ++ msp; Option_edit caption labels value = this.ModColSp labels?value; } ModColSp_widget = ModColSp "HSL"; MorphMeth morph = class Option_string "Method" [ "Correlate", "Convolve", "Dilate", "Erode", "Close", "Open", "DilateIntensity", "ErodeIntensity", "CloseIntensity", "OpenIntensity", "Smooth", "EdgeOut", "EdgeIn", "Edge", "TopHat", "BottomHat", "HitAndMiss", "Thinning", "Thicken", "Distance", "IterativeDistance" ] morph { _flag = morph; Option_edit caption labels value = this.MorphMeth labels?value; } morphmeth_widget = MorphMeth "Dilate"; Noise noise = class Option_string "Noise" [ "Gaussian", "Impulse", "Laplacian", "Multiplicative", "Poisson", "Random", "Uniform" ] noise { _flag = "+noise " ++ noise; Option_edit caption labels value = this.Noise labels?value; } noise_widget = Noise "Gaussian"; Pattern pattern = class Option_string "Noise" [ // See http://www.imagemagick.org/script/formats.php "bricks", "checkerboard", "circles", "crosshatch", "crosshatch30", "crosshatch45", "gray0", "gray5", "gray10", "gray15", "gray20", "gray25", "gray30", "gray35", "gray40", "gray45", "gray50", "gray55", "gray60", "gray65", "gray70", "gray75", "gray80", "gray85", "gray90", "gray95", "gray100", "hexagons", "horizontal", "horizontal2", "horizontal3", "horizontalsaw", "hs_bdiagonal", "hs_cross", "hs_diagcross", "hs_fdiagonal", "hs_horizontal", "hs_vertical", "left30", "left45", "leftshingle", "octagons", "right30", "right45", "rightshingle", "smallfishscales", "vertical", "vertical2", "vertical3", "verticalbricks", "verticalleftshingle", "verticalrightshingle", "verticalsaw" ] pattern { _flag = "pattern:" ++ pattern; Option_edit caption labels value = this.Pattern labels?value; } pattern_widget = Pattern "bricks"; ResizeType resizet = class Option_string "Resize type" [ "resize", "scale", "sample", "adaptive-resize" ] resizet { _flag = resizet; Option_edit caption labels value = this.ResizeType labels?value; } ResizeType_widget = ResizeType "resize"; Size_widget = class { _vislevel = 3; width = Expression "Width (pixels)" 64; height = Expression "Height (pixels)" 64; _flag = "-size " ++ print width.expr ++ "x" ++ print height.expr; }; StatType statt = class Option_string "Statistic type" [ "Gradient", "Maximum", "Mean", "Median", "Minimum", "Mode", "Nonpeak", "StandardDeviation" ] statt { _flag = statt; Option_edit caption labels value = this.StatType labels?value; } StatType_widget = StatType "Mean"; VirtualPixel vp = class Option_string "Virtual pixel" [ "Background", "Black", "CheckerTile", "Dither", "Edge", "Gray", "HorizontalTile", "HorizontalTileEdge", "Mirror", "None", "Random", "Tile", "Transparent", "VerticalTile", "VerticalTileEdge", "White" ] vp { _flag = "-virtual-pixel " ++ vp; _isBackground = (vp == "Background"); Option_edit caption labels value = this.VirtualPixel labels?value; } VirtualPixel_widget = VirtualPixel "Edge"; VirtualPixelBack_widget = class { virtpix = Magick.VirtualPixel_widget; background = Magick.background_widget; _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") ++ virtpix._flag; } Geometry_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; AnnotGeometry_widget = class { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print shearX.expr, "x", print shearY.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; OffsetGeometry_widget = class { _vislevel = 3; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; WhxyGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; FrameGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; outbev = Expression "Outer bevel thickness" 0; inbev = Expression "Inner bevel thickness" 0; _flag = concat [print x.expr, "x", print y.expr, format outbev, format inbev] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } } ================================================ FILE: share/nip2/compat/8.2/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/8.2/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_tile_cache_random x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } decode im = oo_unary_function decode_op im, is_class im = decode_im im, is_image im = error (_ "bad arguments to " ++ "decode") { decode_op = Operator "decode" decode Operator_type.COMPOUND_REWRAP false; decode_im im = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK = im_rad2float im, get_coding im == Image_coding.RAD = im; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; image' = decode image; patches = map (\p extract_area p?0 p?1 sample_width sample_height image') cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } resize interp xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; // is this interpolation nearest-neighbour? is_nn x = x.type == Interpolate_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor, nearest neighbour // upscale by integer part, then affine to exact size = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), xfac' >= 1 && yfac' >= 1 && is_nn interp // downscale by any factor, nearest neighbour // downscale by integer part, then affine to exact size = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 && is_nn interp // upscale by any factor with affine = scale xfac' yfac' im, xfac' >= 1 && yfac' >= 1 // downscale by any factor, bilinear // block shrink by integer factor, then resample to // exact with affine = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), rxfac' >= 1 && ryfac' >= 1 = error ("resize: unimplemented argument combination:\n" ++ " xfac = " ++ print xfac' ++ "\n" ++ " yfac = " ++ print yfac' ++ "\n" ++ " interp = " ++ print interp ++ " (" ++ Interpolate_type.descriptions?interp.type ++ ")") { // convert a float scale to integer plus fraction // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) break f = [floor f, f / floor f]; // same, but for downsizing ... turn a float scale which is less than // 1 into an int shrink and a float scale // complicated: the int shrink may round the size down (eg. imagine // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide // image, not a 3.666 pixel wide image), so pass in the size of image // we are operating on and adjust for any rounding // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) rbreak x f = [int_shrink, float_resample] { // the size of image we are aiming for after the combined int and // float resample x' = x * f; // int part int_shrink = floor (1 / f); // size after int shrink x'' = floor (x / int_shrink); // therefore what we need for the float part float_resample = x' / x''; } width = get_width im; height = get_height im; // grow and shrink factors xg = break xfac'; yg = break yfac'; xs = rbreak width xfac'; ys = rbreak height yfac'; // resize scale xfac yfac im = im_affinei_all im interp.value xfac 0 0 yfac 0 0; } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C * * Also calculate R2, see eg.: * https://en.wikipedia.org/wiki/Coefficient_of_determination */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } ss = len xes; sx = sum xes; sy = sum yes; my = sy / ss; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; my = sy / len xes; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* One image in, one out. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } /* Two images in, one out. */ system_image2 command x1 x2 = oo_binary_function system_image2_op x1 x2, is_class x1 = oo_binary'_function system_image2_op x1 x2, is_class x2 = system x1 x2, is_image x1 && is_image x2 = error (_ "bad arguments to " ++ "system_image2") { system_image2_op = Operator "system_image2" (system_image2 command) Operator_type.COMPOUND_REWRAP false; system x1 x2 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Three images in, one out. */ system_image3 command x1 x2 x3 = oo_binary_function system_image2_op x2 x3, is_class x2 = oo_binary'_function system_image2_op x2 x3, is_class x3 = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 = error (_ "bad arguments to " ++ "system_image3") { system_image2_op = Operator "system_image2" (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; system x1 x2 x3 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2, x3], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Zero images in, one out. */ system_image0 command = Image image_out { [image_out] = vips_call "system" [command] [ $out => true, $out_format => "%s.tif" ]; } hough_line w h x = oo_unary_function hough_line_op x, is_class x = hline (to_real w) (to_real h) x, is_image x = error (_ "bad arguments to " ++ "hough_line") { hough_line_op = Operator "hough_line" (hough_line w h) Operator_type.COMPOUND_REWRAP false; hline w h x = pspace { [pspace] = vips_call "hough_line" [x] [ $width => w, $height => h ]; } } hough_circle s mn mx x = oo_unary_function hough_circle_op x, is_class x = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x = error (_ "bad arguments to " ++ "hough_circle") { hough_circle_op = Operator "hough_circle" (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; hcircle s mn mx x = pspace { [pspace] = vips_call "hough_circle" [x] [ $scale => s, $min_radius => mn, $max_radius => mx ]; } } mapim interp ind in = oo_binary_function mapim_op ind in, is_class ind = oo_binary'_function mapim_op ind in, is_class in = mapim_fn ind in, is_image ind && is_image in = error (_ "bad arguments to " ++ "mapim") { mapim_op = Operator "mapim" (mapim interp) Operator_type.COMPOUND_REWRAP false; mapim_fn ind im = out { [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; } } ================================================ FILE: share/nip2/compat/8.2/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; to_image dpi = extract_bands 0 3 (graph_export_image (to_real dpi) this); } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/8.3/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = print_profile, has_type image && get_type image == Image_type.CMYK && has_bands image && get_bands image >= 4 = monitor_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/8.3/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 2; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 20; fs = Scale "Sharpen flat areas by" 0 5 0.5; js = Scale "Sharpen jaggy areas by" 0 5 1; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_hough_item = class Menupullright "_Hough Transform" "transform to parameter space" { Line_item = class Menuaction "_Line" "find straight line Hough transform" { action a = class _result { _vislevel = 3; pspace_width = Expression "Parameter space width" 64; pspace_height = Expression "Parameter space height" 64; _result = map_unary line a { line a = hough_line (to_real pspace_width) (to_real pspace_height) a; } } } Circle_item = class Menuaction "_Circle" "find circle Hough transform" { action a = class _result { _vislevel = 3; scale = Expression "Scale down parameter space by" 10; min_radius = Expression "Minimum radius" 10; max_radius = Expression "Maximum radius" 30; _result = map_unary circle a { circle a = hough_circle (to_real scale) (to_real min_radius) (to_real max_radius) a; } } } } Filter_coordinate_item = class Menupullright "_Coordinate Transform" "various coordinate transforms" { // run a function which wants a complex arg on a non-complex two-band // image run_cmplx fn x = re x' ++ im x' { x' = fn (x?0, x?1); } Polar_item = class Menuaction "_Polar" "transform to polar coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_polar a { to_polar im = mapim interp.value map' im { // xy image, origin in the centre, scaled to fit image to // a circle xy = make_xy im.width im.height; xy' = xy - Vector [im.width / 2, im.height / 2]; scale = min [im.width, im.height] / im.width; xy'' = 2 * xy' / scale; // to polar, scale vertical axis to 360 degrees map = run_cmplx polar xy''; map' = map * Vector [1, im.height / 360]; } } } } Rectangular_item = class Menuaction "_Rectangular" "transform to rectangular coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_rect a { to_rect im = mapim interp.value map'' im { // xy image, vertical scaled to 360 degrees xy = make_xy im.width im.height; xy' = xy * Vector [1, 360 / im.height]; // to rect, scale to image rect map = run_cmplx rectangular xy'; scale = min [im.width, im.height] / im.width; map' = map * scale / 2; map'' = map' + Vector [im.width / 2, im.height / 2]; } } } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } Autotrace_item = class Menuaction "_Trace" "convert a bitmap to an SVG file" { action x = class _result { _vislevel = 3; despeckle = Scale "Despeckle level" 1 20 1; line = Scale "Line threshold" 1 20 1; center = Toggle "Trace centreline" false; scale = Scale "SVG scale" 0.1 10 1; command = "autotrace %s " ++ join_sep " " [ofmt, ofile, desp, lint, cent] { prog = search_for_error "autotrace"; ofmt = "-output-format svg"; ofile = "-output-file %s"; desp = "-despeckle-level " ++ print despeckle.value; lint = "-line-threshold " ++ print line.value; cent = if center then "-centerline " else ""; } _result = Image output { [output] = vips_call "system" [command] [$in => [x.value], $in_format => "%s.ppm", $out => true, $out_format => "%s.svg[scale=" ++ print scale.value ++ "]" ]; } } } ================================================ FILE: share/nip2/compat/8.3/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Kernel_linear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; caption = Expression "Chart caption" "none"; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; xcaption = Expression "X axis caption" "none"; ycaption = Expression "Y axis caption" "none"; series_captions = Expression "Series captions" ["Band 0"]; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range ++ captions; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; captions = concat (map test caption_options) ++ [$series_captions => series_captions.expr] { caption_options = [ $caption => caption.expr, $xcaption => xcaption.expr, $ycaption => ycaption.expr ]; test x = [], value == "none" = [option_name => value] { [option_name, value] = x; } } image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/8.3/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel h v image, aspect = resize kernel fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize kernel fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_map_item = class Menuaction "_Map" "map an image through a 2D transform image" { action a b = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_binary trans a b { trans a b = mapim interp.value in index { // get the index image first [index, in] = sortc (const is_twocomponent) [a, b]; // is a two-component image, ie. one band complex, or // two-band non-complex is_twocomponent x = is_nonc x || is_c x; is_nonc x = has_bands x && get_bands x == 2 && has_format x && !is_complex_format (get_format x); is_c x = has_bands x && get_bands x == 1 && has_format x && is_complex_format (get_format x); is_complex_format f = f == Image_format.COMPLEX || f == Image_format.DPCOMPLEX; } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 100; sy = Expression "Start y" 100; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/8.3/Magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate Magick.def 13-Apr-2014 snibgo Put "new image" items into sub-menu. New class VirtualPixlBack. 17-Apr-2014 snibgo Many small changes. A few new menu options. Created sub-menu for multi-input operations. 3-May-2014 jcupitt Put quotes around ( in shadow to help unix Last update: 17-Apr-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ // We don't need Noop. /*=== Magick_noop_item = class Menuaction "_Noop" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_testPar_item = class Menuaction "_TestPar" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "( +clone ) +append ", "\"%s\"" ]; _result = Magick.system command x; } } /* Removed Read_item and Write_item, much better to use nip2 load/save image. * Plus they can load all libMagick formats anyway. */ // Put "new image" items into sub-menu Magick_NewImageMenu_item = class Menupullright "_New image" "make a new image" { Magick_newcanvas_item = class Menuaction "_Solid colour" "make image of solid colour" { action = class _result { _vislevel = 3; size = Magick.Size_widget; colour = Magick.generalcol_widget; command = Magick.command [ size._flag, "\"canvas:" ++ colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_builtin_item = class Menuaction "_Built-in image" "create a built-in image" { action = class _result { _vislevel = 3; builtin = Magick.builtin_widget; command = Magick.command [ builtin._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_gradient_item = class Menuaction "_Gradient" "make a linear gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; topColour = Magick.generalcol_widget; bottomColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"gradient:", topColour._flag, "-", bottomColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } Magick_hald_item = class Menuaction "_Hald-clut image" "create an identity hald-clut image" { action = class _result { _vislevel = 3; order = Expression "order" 8; command = Magick.command [ "hald:" ++ print order.expr, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_pattern_item = class Menuaction "_Pattern" "create pattern" { action = class _result { _vislevel = 3; size = Magick.Size_widget; pattern = Magick.pattern_widget; command = Magick.command [ size._flag, pattern._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_plasma_item = class Menuaction "_Plasma image" "create plasma image" { action = class _result { _vislevel = 3; size = Magick.Size_widget; // FIXME? ColourA-ColourB. // FIXME? Allow plasma:fractal? command = Magick.command [ size._flag, "plasma:", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_radialgradient_item = class Menuaction "_Radial gradient" "make a radial gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; innerColour = Magick.generalcol_widget; outerColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"radial-gradient:", innerColour._flag, "-", outerColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } } // end Magick_NewImageMenu_item Magick_MultiMenu_item = class Menupullright "_Multiple inputs" "make an image from multiple images" { Magick_composite_item = class Menuaction "_Composite" "composite two images (without mask)" { action x y = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_compositeMask_item = class Menuaction "_Composite masked" "composite two images (with mask)" { action x y z = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system3 command x y z; } } // FIXME: other operations like remap that take another image as arguments are: // mask (pointless?), texture, tile (pointless?) // FIXME: operations that take a filename that isn't an image: // cdl, profile Magick_clut_item = class Menuaction "_Clut" "replace values using second image as colour look-up table" { action x y = class _result { _vislevel = 3; // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" command = Magick.command [ "\"%s\"", "\"%s\"", "-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_haldclut_item = class Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"", "-hald-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } // Encipher and decipher: key files can be text or image files. Magick_encipher_item = class Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { action x = class _result { _vislevel = 3; pathname = Pathname "Read key file" ""; isDecipher = Toggle "Decipher" false; command = Magick.command [ "\"%s\"", if pathname.value == "" then "" else ( ( if isDecipher then "-decipher " else "-encipher ") ++ ( "\"" ++ pathname.value ++ "\"" ) ), "\"%s\"" ]; _result = Magick.system command x; } } Magick_profile_item = class Menuaction "_Profile" "assigns/applies an ICC profile" { action x = class _result { _vislevel = 3; pathname1 = Pathname "Read profile file" ""; pathname2 = Pathname "Read profile file" ""; command = Magick.command [ "\"%s\"", if pathname1.value == "" then "" else ( "-profile " ++ "\"" ++ pathname1.value ++ "\""), if pathname2.value == "" then "" else ( "-profile " ++ "\"" ++ pathname2.value ++ "\""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_remap_item = class Menuaction "_Remap" "reduce colours to those in another image" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-remap", "\"%s\"", "\"%s\"" ]; _result = Magick.system2 command x y; } } } // end Magick_MultiMenu_item Magick_image_type_item = class Menuaction "_Image Type" "change image type" { action x = class _result { _vislevel = 3; imagetype = Magick.imagetype_widget; command = Magick.command [ "\"%s\"", imagetype._flag, "\"%s\"" ]; _result = Magick.system command x; } } sep2 = Menuseparator; Magick_alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = Magick.alpha_widget; command = Magick.command [ "\"%s\"", alpha._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = Magick.text_widget; font = Magick.Font_widget; geometry = Magick.AnnotGeometry_widget; gravity = Magick.gravity_widget; foreground = Magick.foreground_widget; undercol = Magick.undercol_widget; antialias = Magick.antialias_widget; command = Magick.command [ "\"%s\"", font._flag, antialias._flag, gravity._flag, foreground._flag, undercol._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoGamma_item = class Menuaction "_AutoGamma" "automatic gamma" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-gamma", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoLevel_item = class Menuaction "_AutoLevel" "automatic level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-level", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blurSharpMenu_item = class Menupullright "_Blur/Sharpen" "blur and sharpen" { Magick_adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; // note: adaptive-blur doesn't regard VP. command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-adaptive-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_blur_item = class Menuaction "_Blur" "blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gaussianBlur_item = class Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-gaussian-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_motionBlur_item = class Menuaction "_Motion Blur" "simulate motion blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; channels = Magick.ch_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-motion-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotationalBlur_item = class Menuaction "_RotationalBlur" "blur around the centre" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; angle = Scale "angle (degrees)" (-360) 360 20; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-radial-blur", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_selectiveBlur_item = class Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; threshold = Scale "Threshold (percent)" 0 100 50; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", intensity._flag, virtpixback._flag, channels._flag, "-selective-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print threshold.value ++ "%%", "\"%s\"" ]; _result = Magick.system command x; } } sep1 = Menuseparator; Magick_adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-adaptive-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sharpen_item = class Menuaction "_Sharpen" "sharpen" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_unsharpen_item = class Menuaction "_Unsharp" "sharpen with unsharp mask" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; gain = Scale "Gain" (-10) 10 1; threshold = Scale "Threshold" 0 1 0.05; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-unsharp", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print gain.value ++ "+" ++ print threshold.value, "\"%s\"" ]; _result = Magick.system command x; } } } // end BlurSharpMenu_item Magick_border_item = class Menuaction "_Border" "add border of given colour" { action x = class _result { _vislevel = 3; compose = Magick.compose_widget; width = Expression "Width" 3; bordercol = Magick.bordercol_widget; command = Magick.command [ "\"%s\"", compose._flag, bordercol._flag, "-border", print width.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_brightCont_item = class Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { action x = class _result { _vislevel = 3; bri = Scale "brightness" (-100) 100 0; con = Scale "contrast" (-100) 100 0; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-brightness-contrast", print bri.value ++ "x" ++ print con.value, "\"%s\"" ]; _result = Magick.system command x; } } // Note: canny requires ImageMagick 6.8.9-0 or later. Magick_canny_item = class Menuaction "_Canny" "detect a wide range of edges" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; lowPc = Scale "lower percent" 0 100 10; highPc = Scale "lower percent" 0 100 10; command = Magick.command [ "\"%s\"", "-canny", concat ["\"", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print lowPc.value ++ "%%+" ++ print highPc.value ++ "%%" ++ "\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_charcoal_item = class Menuaction "_Charcoal" "simulate a charcoal drawing" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; factor = Scale "factor" 0 50 1; command = Magick.command [ "\"%s\"", intensity._flag, "-charcoal", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_chop_item = class Menuaction "_Chop" "remove pixels from the interior" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-chop", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorize_item = class Menuaction "_Colorize" "colorize by given amount" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; val = Scale "value" 0 100 100; command = Magick.command [ "\"%s\"", foreground._flag, "-colorize", print val.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colors_item = class Menuaction "_Colors" "reduce number of colors" { action x = class _result { _vislevel = 3; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; quantize = Magick.colorspace_widget; colors = Expression "Colours" 3; command = Magick.command [ "\"%s\"", "-quantize", quantize._flag, "-treedepth", print treedepth.expr, dither._flag, "-colors", print colors.expr, "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: color-matrix? Magick_colorspace_item = class Menuaction "_Colourspace" "convert to arbitrary colourspace" { action x = class _result { _vislevel = 3; colsp = Magick.colorspace_widget; command = Magick.command [ "\"%s\"", "-colorspace", colsp._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorspaceGray_item = class Menuaction "_Colourspace gray" "convert to gray using given intensity method" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-colorspace gray", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrast_item = class Menuaction "_Contrast" "increase or reduce the contrast" { action x = class _result { _vislevel = 3; isReduce = Toggle "reduce contrast" false; command = Magick.command [ "\"%s\"", (if isReduce then "+" else "-") ++ "contrast", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrastStretch_item = class Menuaction "_Contrast stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-contrast-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: convolve (bias, kernel) Magick_crop_item = class Menuaction "_Crop" "cut out a rectangular region" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-crop", geometry._flag, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_deskew_item = class Menuaction "_Deskew" "straighten the image" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; // FIXME: toggle auto-crop? command = Magick.command [ "\"%s\"", "-deskew", "\"" ++ print threshold.value ++ "%%\"", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_despeckle_item = class Menuaction "_Despeckle" "reduce the speckles" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-despeckle", "\"%s\"" ]; _result = Magick.system command x; } } Magick_distort_item = class Menuaction "_Distort" "distort using a method and arguments" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; distort = Magick.distort_widget; args = String "Arguments" "1,0"; isPlus = Toggle "Extend to show entire image" false; command = Magick.command [ "\"%s\"", virtpixback._flag, (if isPlus then "+" else "-") ++ "distort", distort._flag, args.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_draw_item = class Menuaction "_Draw" "annotate with one or more graphic primitives" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; command = Magick.command [ "\"%s\"", foreground._flag, "-draw", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_edge_item = class Menuaction "_Edge" "detect edges" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-edge", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_emboss_item = class Menuaction "_Emboss" "emboss" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-emboss", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_enhance_item = class Menuaction "_Enhance" "enhance a noisy image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-enhance", "\"%s\"" ]; _result = Magick.system command x; } } Magick_equalize_item = class Menuaction "_Equalize" "equalize the histogram" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-equalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_evaluate_item = class Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; operation = Magick.evaluate_widget; val = Expression "value" 5; isPc = Toggle "Value is percent" false; command = Magick.command [ "\"%s\"", channels._flag, operation._flag, print val.expr ++ if isPc then "%%" else "", "\"%s\"" ]; _result = Magick.system command x; } } Magick_extent_item = class Menuaction "_Extent" "set the image size and offset" { action x = class _result { _vislevel = 3; background = Magick.background_widget; compose = Magick.compose_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, background._flag, gravity._flag, "-extent", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_FlipFlopMenu_item = class Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { Magick_flip_item = class Menuaction "_Flip vertically" "mirror upside-down" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flip", "\"%s\"" ]; _result = Magick.system command x; } } Magick_flop_item = class Menuaction "_Flop horizontally" "mirror left-right" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flop", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transpose_item = class Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transpose +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transverse_item = class Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transverse +repage", "\"%s\"" ]; _result = Magick.system command x; } } } // end Magick_FlipFlopMenu_item Magick_floodfill_item = class Menuaction "_Floodfill" "recolour neighbours that match" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; fuzz = Magick.fuzz_widget; coordinate = Magick.coordinate_widget; // -draw "color x,y floodfill" command = Magick.command [ "\"%s\"", foreground._flag, "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-draw \" color", coordinate._flag, "floodfill \"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_frame_item = class Menuaction "_Frame" "surround with border or beveled frame" { action x = class _result { _vislevel = 3; border = Magick.bordercol_widget; compose = Magick.compose_widget; matte = Magick.mattecol_widget; geometry = Magick.FrameGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, border._flag, matte._flag, "-frame", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_function_item = class Menuaction "_Function" "evaluate a function on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; function = Magick.function_widget; // FIXME: explain values; use sensible defaults. values = String "values" "0,0,0,0"; command = Magick.command [ "\"%s\"", channels._flag, "-function", function._flag, values.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_fx_item = class Menuaction "_Fx" "apply a mathematical expression" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; interpolate = Magick.interpolate_widget; virtpixback = Magick.VirtualPixelBack_widget; args = String "Expression" "u*1/2"; command = Magick.command [ "\"%s\"", channels._flag, interpolate._flag, virtpixback._flag, "-fx", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_gamma_item = class Menuaction "_Gamma" "apply a gamma correction" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; gamma = Magick.gamma_widget; command = Magick.command [ "\"%s\"", channels._flag, "-gamma", print gamma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradient_item = class Menuaction "_Gradient" "apply a linear gradient" { action x = class _result { _vislevel = 3; colourA = Magick.generalcol_widget; colourB = Magick.generalcol_widget; position = Option "colourA is at" [ "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"] 0; _baryArg = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 = "dunno"; command = Magick.command [ "\"%s\"", "-sparse-color barycentric \"" ++ _baryArg ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradientCorn_item = class Menuaction "_Gradient corners" "apply a bilinear gradient between the corners" { action x = class _result { _vislevel = 3; colour_top_left = Magick.generalcol_widget; colour_top_right = Magick.generalcol_widget; colour_bottom_left = Magick.generalcol_widget; colour_bottom_right = Magick.generalcol_widget; command = Magick.command [ "\"%s\"", "-sparse-color bilinear \"" ++ "0,0," ++ colour_top_left._flag ++ ",%%[fx:w-1],0" ++ colour_top_right._flag ++ ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", "+depth", "\"%s\"" ]; _result = Magick.system command x; } } Magick_histogram_item = class Menuaction "_Histogram" "make a histogram image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-define histogram:unique-colors=false histogram:" ++ "\"%s\"" ]; _result = Magick.system command x; } } Magick_implode_item = class Menuaction "_Implode" "implode pixels about the center" { action x = class _result { _vislevel = 3; factor = Scale "factor" 0 20 1; interpolate = Magick.interpolate_widget; // FIXME: virtual-pixel? command = Magick.command [ "\"%s\"", interpolate._flag, "-implode", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_level_item = class Menuaction "_Level" "adjust the level of channels" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "black point" (-100) 200 0; wht = Scale "white point" (-100) 200 100; gam = Scale "gamma" 0 30 1; isPc = Toggle "Levels are percent" true; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level", "\"" ++ print blk.value ++ "," ++ print wht.value ++ (if isPc then "%%" else "") ++ "," ++ print gam.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_levelCols_item = class Menuaction "_Level colors" "adjust levels to given colours" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; colour_black = Magick.generalcol_widget; colour_white = Magick.generalcol_widget; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level-colors", "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_linearStretch_item = class Menuaction "_Linear stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", channels._flag, "-linear-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_magnify_item = class Menuaction "_Magnify" "double the size of the image with pixel art scaling" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-magnify", "\"%s\"" ]; _result = Magick.system command x; } } Magick_modulate_item = class Menuaction "_Modulate" "modulate brightness, saturation and hue" { action x = class _result { _vislevel = 3; modcolsp = Magick.ModColSp_widget; bright = Scale "brightness" 0 200 100; sat = Scale "saturation" 0 200 100; hue = Scale "hue" 0 200 100; command = Magick.command [ "\"%s\"", modcolsp._flag, "-modulate", print bright.value ++ "," ++ print sat.value ++ "," ++ print hue.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_monochrome_item = class Menuaction "_Monochrome" "transform to black and white" { action x = class _result { _vislevel = 3; // FIXME: also intensity? intensity = Magick.intensity_widget; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; command = Magick.command [ "\"%s\"", intensity._flag, dither._flag, "-treedepth", print treedepth.expr, "-monochrome", "\"%s\"" ]; _result = Magick.system command x; } } Magick_morphology_item = class // See http://www.imagemagick.org/Usage/morphology/ Menuaction "_Morphology" "apply a morphological method" { action x = class _result { _vislevel = 3; method = Magick.morphmeth_widget; iter = Expression "Iterations (-1=repeat until done)" 1; kernel = Magick.kernel_widget; // FIXME: custom kernel eg "3x1+2+0:1,0,0" // width x height + offsx + offsy : {w*h values} // each value is 0.0 to 1.0 or "NaN" or "-" // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 // but // ring takes: radius1, radius2, scale // rectangle and comet take: width x height + offsx + offsy // blur takes: radius x sigma // FIXME: for now, simply allow any string input. kernel_arg = String "Kernel arguments" ""; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-morphology", method._flag ++ ":" ++ print iter.expr, kernel._flag ++ (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_negate_item = class Menuaction "_Negate" "negate" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-negate", "\"%s\"" ]; _result = Magick.system command x; } } Magick_addNoise_item = class Menuaction "_add Noise" "add noise" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; attenuate = Scale "attenuate" 0 1.0 1.0; noise = Magick.noise_widget; command = Magick.command [ "\"%s\"", channels._flag, "-attenuate", print attenuate.value, noise._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_normalize_item = class Menuaction "_Normalize" "normalize" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-normalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_opaque_item = class Menuaction "_Opaque" "change this colour to the fill colour" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; fill = Magick.foreground_widget; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", fill._flag, channels._flag, "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "opaque", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_paint_item = class Menuaction "_Paint" "simulate an oil painting" { action x = class _result { _vislevel = 3; rad = Expression "radius" 10; command = Magick.command [ "\"%s\"", "-paint", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } /*=== FIXME Bug; remove for now. Polaroid_item = class Menuaction "_Polaroid" "simulate a polaroid picture" { action x = class _result { _vislevel = 3; angle = Scale "angle" (-90) 90 20; command = Magick.command [ "\"%s\"", "-polaroid", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_posterize_item = class Menuaction "_Posterize" "reduce to (n) levels per channel" { action x = class _result { _vislevel = 3; levels = Expression "levels" 3; command = Magick.command [ "\"%s\"", "-posterize", print levels.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_raise_item = class Menuaction "_Raise" "lighten or darken image edges" { action x = class _result { _vislevel = 3; thk = Expression "Thickness" 3; command = Magick.command [ "\"%s\"", "-raise", print thk.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_resize_item = class Menuaction "_Resize" "resize to given width and height" { action x = class _result { _vislevel = 3; filter = Magick.filter_widget; type = Magick.ResizeType_widget; width = Scale "Width" 1 100 10; height = Scale "Height" 1 100 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", filter._flag, "-" ++ type._flag, "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "!") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_roll_item = class Menuaction "_Roll" "roll an image horizontally or vertically" { action x = class _result { _vislevel = 3; rollx = Expression "X" 3; rolly = Expression "Y" 3; command = Magick.command [ "\"%s\"", "-roll", (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotate_item = class Menuaction "_Rotate" "rotate" { action x = class _result { _vislevel = 3; angle = Scale "angle (degrees)" (-360) 360 20; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, "+distort", "SRT", print angle.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -segment, but cluster-threshold should be percentage of image area. Magick_sepia_item = class Menuaction "_Sepia tone" "simulate a sepia-toned photo" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; command = Magick.command [ "\"%s\"", "-sepia-tone", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shade_item = class Menuaction "_Shade" "shade with a distant light source" { action x = class _result { _vislevel = 3; azimuth = Scale "Azimuth (degrees)" (-360) 360 0; elevation = Scale "Elevation (degrees)" 0 90 45; command = Magick.command [ "\"%s\"", "-shade", print azimuth.value ++ "x" ++ print elevation.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_shadow_item = class Menuaction "_Shadow" "simulate a shadow" { action x = class _result { _vislevel = 3; shadowCol = Magick.generalcol_widget; opacity = Scale "Opacity (percent)" 0 100 75; sigma = Scale "Sigma" 0 30 2; // FIXME: make offsets a single widget? offsx = Scale "X-offset" (-20) 20 4; offsy = Scale "Y-offset" (-20) 20 4; arePc = Toggle "offsets are percentages" false; // FIXME: raw operation creates page offset, which vips dislikes. // So we take this futher, compositing with source. command = Magick.command [ "\"%s\"", "\"(\" +clone", "-background", "\"" ++ shadowCol._flag ++ "\"", "-shadow", concat [ "\"", print opacity.value, "x", print sigma.value, (if offsx.value >= 0 then "+" else ""), print offsx.value, (if offsy.value >= 0 then "+" else ""), print offsy.value, (if arePc then "%%" else ""), "\"" ], "\")\" +swap -background None -layers merge", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shave_item = class Menuaction "_Shave" "shave pixels from the edges" { action x = class _result { _vislevel = 3; width = Scale "Width" 0 50 10; height = Scale "Height" 0 50 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", "-shave", "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shear_item = class Menuaction "_Shear" "shear along the x-axis and/or y-axis" { action x = class _result { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-shear", print shearX.expr ++ "x" ++ print shearY.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sigmoid_item = class Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; contrast = Scale "contrast" 0 30 3; midpoint = Scale "mid-point (percent)" 0 100 50; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "sigmoidal-contrast", "\"" ++ print contrast.value ++ "x" ++ print midpoint.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_sketch_item = class Menuaction "_Sketch" "simulate a pencil sketch" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; command = Magick.command [ "\"%s\"", "-sketch", print radius.value ++ "x" ++ print sigma.value ++ (if angle >= 0 then ("+" ++ print angle.value) else ""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_solarize_item = class Menuaction "_Solarize" "negate all pixels above a threshold level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-solarize", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -sparse-color needs abitrary list of {x,y,colour}. Magick_splice_item = class Menuaction "_Splice" "splice a colour into the image" { action x = class _result { _vislevel = 3; background = Magick.background_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", background._flag, gravity._flag, "-splice", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_spread_item = class Menuaction "_Spread" "displace pixels by random amount" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; amount = Expression "Amount (pixels)" 5; command = Magick.command [ "\"%s\"", virtpixback._flag, "-spread", print amount.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_statistic_item = class Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { action x = class _result { _vislevel = 3; width = Expression "Width" 5; height = Expression "Height" 5; statisticType = Magick.StatType_widget; command = Magick.command [ "\"%s\"", "-statistic", statisticType._flag, print width.expr ++ "x" ++ print height.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = Magick.angle_widget; command = Magick.command [ "\"%s\"", "-swirl", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_thresholdMenu_item = class Menupullright "_Threshold" "make black or white" { Magick_threshold_item = class Menuaction "_Threshold" "apply black/white threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blackThreshold_item = class Menuaction "_Black threshold" "where below threshold set to black" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-black-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_whiteThreshold_item = class Menuaction "_White threshold" "where above threshold set to white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-white-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_latThreshold_item = class Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { action x = class _result { _vislevel = 3; width = Expression "Width" 10; height = Expression "Height" 10; offset = Scale "Offset (percent)" (-100) 100 0; // note: "-lat" doesn't respond to channels command = Magick.command [ "\"%s\"", "-lat", concat ["\"", print width.expr, "x", print height.expr, (if offset.value >= 0 then "+" else ""), print offset.value, "%%\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_randThreshold_item = class Menuaction "_Random Threshold" "between specified limits, apply random threshold" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; low = Scale "Low threshold" 0 100 10; high = Scale "High threshold" 0 100 90; isPc = Toggle "Thresholds are percent" true; command = Magick.command [ "\"%s\"", channels._flag, "-random-threshold", "\"" ++ print low.value ++ "x" ++ print high.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } } // end ThresholdMenu_item // Note: alternatives include: // convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif Magick_tile_item = class Menuaction "_Tile" "fill given size with tiled image" { action x = class _result { _vislevel = 3; size = Magick.Size_widget; command = Magick.command [ size._flag, "tile:" ++ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_tint_item = class Menuaction "_Tint" "apply a tint" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; amount = Scale "amount (percent)" 0 100 50; command = Magick.command [ "\"%s\"", foreground._flag, "-tint", // snibgo note: although the amount is a percentage, it doesn't need "%" character. print amount.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_transparent_item = class Menuaction "_Transparent" "make this colour transparent" { action x = class _result { _vislevel = 3; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "transparent", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_trim_item = class Menuaction "_Trim" "trims away border" { action x = class _result { _vislevel = 3; fuzz = Magick.fuzz_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-trim +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_uniqueCols_item = class Menuaction "_Unique colours" "discard all but one of any pixel color" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-unique-colors", "\"%s\"" ]; _result = Magick.system command x; } } Magick_vignette_item = class Menuaction "_Vignette" "soften the edges in vignette style" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; rx = Scale "Rolloff x (percent)" 0 100 10; ry = Scale "Rolloff y (percent)" 0 100 10; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-vignette", print radius.value ++ "x" ++ print sigma.value ++ (if rx.value >= 0 then "+" else "") ++ print rx.value ++ (if ry.value >= 0 then "+" else "") ++ print ry.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_wave_item = class Menuaction "_Wave" "shear the columns into a sine wave" { action x = class _result { _vislevel = 3; amplitude = Scale "Amplitude (pixels)" 0 100 10; wavelength = Scale "Wavelength (pixels)" 0 100 10; command = Magick.command [ "\"%s\"", "-wave", print amplitude.value ++ "x" ++ print wavelength.value, "\"%s\"" ]; _result = Magick.system command x; } } ================================================ FILE: share/nip2/compat/8.3/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/8.3 start_DATA = \ Colour.def \ _convert.def \ Filter.def \ _generate.def \ Histogram.def \ Image.def \ _joe_extra.def \ _joe_utilities.def \ _list.def \ _magick.def \ Magick.def \ Math.def \ Matrix.def \ _Object.def \ Object.def \ _predicate.def \ Preferences.ws \ _stdenv.def \ Tasks.def \ _types.def \ Widgets.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/8.3/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/8.3/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/8.3/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/8.3/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/8.3/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Kernel_linear xfactor yfactor scale_im; _offset_im = resize Kernel_linear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/8.3/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/8.3/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/8.3/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart > 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [RGB16, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; /* Return $PATH, reformatted as [["comp1", "comp2"], ["comp1", "comp2"] ..] */ system_search_path = [vipsbin] ++ map path_parse (split (equal path_sep) (expand "$PATH")) { /* On some platforms we ship vips with a few extra progs. Search * $VIPSHOME/bin first. */ vipsbin = path_parse (expand "$VIPSHOME") ++ ["bin"]; path_sep = ':', expand "$SEP" == "/" = ';'; } /* Search $PATH for the first occurence of name, or "". */ search_for name = hits?0, hits != [] = "" { exe_name = name ++ expand "$EXEEXT"; form_path p = path_absolute (p ++ [exe_name]); paths = map form_path system_search_path; hits = dropwhile (equal []) (map search paths); } /* Search $PATH for the first occurence of name, error on failure. */ search_for_error name = path, path != "" = error (exe_name ++ " not found on your search path. " ++ "Check you have installed the program and it is on your PATH.") { exe_name = name ++ expand "$EXEEXT"; path = search_for name; } ================================================ FILE: share/nip2/compat/8.3/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/compat/8.3/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Kernel_linear f1 1 b1 {b1 = resize Kernel_linear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/8.3/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/8.3/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/8.3/_magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate _magick.def Add 0-ary and 2-ary system Put utility funcs into a Magick class 11-Apr-2014 snibgo Added VirtualPixelBack for cases where background is only relevant when VP=Background 17-Apr-2014 snibgo Many small changes. 2-May-2014 jcupitt Added Magick.version 30-June-2014 Put single-quotes around command exe to help win 1-July-2014 Automatically fall back to gm if we can't find convert 17-July-2014 better GM support Last update: 17-July-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ /* Put these in a class to avoid filling the main namespace with IM stuff. */ Magick = class { // first gm on path, or "" gm_path = search_for "gm"; // first convert on $PATH, or "" // we check for the convert we ship first convert_path = vips_convert, vips_convert != "" = search_for "convert" { // the convert we ship with the vips binary on some platforms, or "" vips_convert = search (path_absolute convert) { vipshome = path_parse (expand "$VIPSHOME"); convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; } } use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; // Are we in GM or IM mode? use_gm = true, use_gm_pref && gm_path != "" = false, !use_gm_pref && convert_path != "" = false, convert_path != "" = true, gm_path != "" = error "neither IM nor GM executable found"; command_path = gm_path, use_gm = convert_path; // try to get the version as eg. [6, 7, 7, 10] // GM versions are smaller, typically [1, 3, 18] version = map parse_int (split (member ".-") version_string) { [output] = vips_call "system" ["'" ++ command_path ++ "' -version"] [$log=>true]; version_string = (split (equal ' ') output)?1, use_gm = (split (equal ' ') output)?2; } // make a command-line ... args is a [str] we join with spaces command args = "'" ++ command_path ++ "' " ++ join_sep " " args' { args' = ["convert"] ++ args, use_gm = args; } // capabilities ... different versions support different features, we // turn features on and off based on these // would probably be better to test for caps somehow has_intensity = false, use_gm = version?0 > 6 || version?1 > 7; has_channel = false, use_gm = version?0 > 6 || version?1 > 7; system0 cmd = system_image0 cmd; system cmd x = map_unary (system_image cmd) x; system2 cmd x y = map_binary (system_image2 cmd) x y; system3 cmd x y z = map_trinary (system_image3 cmd) x y z; radius_widget = Scale "Radius" 0 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle (degrees)" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; gamma_widget = Scale "Gamma" 0 10 1; colors_widget = Scale "Colors" 1 10 3; resize_widget = Scale "Resize (percent)" 0 500 100; fuzz_widget = Scale "Fuzz (percent)" 0 100 0; blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; // a colour with no enclosing quotes ... use this if we know there are // some quotes at an outer level print_colour_nq triple = concat ["#", concat (map fmt triple)] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } // we need the quotes because # is the comment character in *nix print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; GeneralCol triple = class Colour "sRGB" triple { _flag = print_colour_nq triple; Colour_edit space triple = this.GeneralCol triple; } generalcol_widget = GeneralCol [0, 0, 0]; Background triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" false; _flag = "-background " ++ if isNone then "None" else print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Bordercol triple = class Colour "sRGB" triple { _flag = "-bordercolor " ++ print_colour triple; Colour_edit space triple = this.Bordercol triple; } bordercol_widget = Bordercol [0, 0, 0]; Mattecol triple = class Colour "sRGB" triple { _flag = "-mattecolor " ++ print_colour triple; Colour_edit space triple = this.Mattecol triple; } mattecol_widget = Mattecol [189, 189, 189]; // FIXME: Undercolour, like many others, can have alpha channel. // How does user input this? With a slider? Undercol triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" true; _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); Colour_edit space triple = this.Undercol triple; } undercol_widget = Undercol [0, 0, 0]; changeCol_widget = class { _vislevel = 3; colour = GeneralCol [0, 0, 0]; fuzz = fuzz_widget; nonMatch = Toggle "change non-matching colours" false; } Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Remove", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Builtin builtin = class Option_string "Builtin" [ // See http://www.imagemagick.org/script/formats.php "rose:", "logo:", "wizard:", "granite:", "netscape:" ] builtin { _flag = builtin; Option_edit caption labels value = this.Builtin labels?value; } builtin_widget = Builtin "rose:"; channels_widget = class { // FIXME? Can we grey-out alpha when we have no alpha channel, // show CMY(K) instead of RGB(K) etc? // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. ChanR valueR = class Toggle "Red" valueR { _flag = "R", valueR = ""; Toggle_edit caption valueR = this.ChanR valueR; } channelR = ChanR true; ChanG valueG = class Toggle "Green" valueG { _flag = "G", valueG = ""; Toggle_edit caption valueG = this.ChanG valueG; } channelG = ChanG true; ChanB valueB = class Toggle "Blue" valueB { _flag = "B", valueB = ""; Toggle_edit caption valueB = this.ChanB valueB; } channelB = ChanB true; ChanK valueK = class Toggle "Black" valueK { _flag = "K", valueK = ""; Toggle_edit caption valueK = this.ChanK valueK; } channelK = ChanK true; ChanA valueA = class Toggle "Alpha" valueA { _flag = "A", valueA = ""; Toggle_edit caption valueA = this.ChanA valueA; } channelA = ChanA false; ChanSy valueSy = class Toggle "Sync" valueSy { _flag = ",sync", valueSy = ""; Toggle_edit caption valueSy = this.ChanSy valueSy; } channelSy = ChanSy true; _rgbka = concat [channelR._flag, channelG._flag, channelB._flag, channelK._flag, channelA._flag ]; _flag = "", _rgbka == "" || !has_channel = concat [ "-channel ", _rgbka, channelSy._flag ]; } ch_widget = channels_widget; Colorspace colsp = class Option_string "Colorspace" [ "CIELab", "CMY", "CMYK", "Gray", "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "Lab", "LCH", "LCHab", "LCHuv", "LMS", "Log", "Luv", "OHTA", "Rec601Luma", "Rec601YCbCr", "Rec709Luma", "Rec709YCbCr", "RGB", "scRGB", "sRGB", "Transparent", "XYZ", "YCbCr", "YDbDr", "YCC", "YIQ", "YPbPr", "YUV" ] colsp { _flag = colsp; Option_edit caption labels value = this.Colorspace labels?value; } colorspace_widget = Colorspace "sRGB"; Compose comp = class Option_string "Compose method" [ "Atop", "Blend", "Blur", "Bumpmap", "ChangeMask", "Clear", "ColorBurn", "ColorDodge", "Colorize", "CopyBlack", "CopyBlue", "CopyCyan", "CopyGreen", "Copy", "CopyMagenta", "CopyOpacity", "CopyRed", "CopyYellow", "Darken", "DarkenIntensity", "DivideDst", "DivideSrc", "Dst", "Difference", "Displace", "Dissolve", "Distort", "DstAtop", "DstIn", "DstOut", "DstOver", "Exclusion", "HardLight", "Hue", "In", "Lighten", "LightenIntensity", "LinearBurn", "LinearDodge", "LinearLight", "Luminize", "Mathematics", "MinusDst", "MinusSrc", "Modulate", "ModulusAdd", "ModulusSubtract", "Multiply", "None", "Out", "Overlay", "Over", "PegtopLight", "PinLight", "Plus", "Replace", "Saturate", "Screen", "SoftLight", "Src", "SrcAtop", "SrcIn", "SrcOut", "SrcOver", "VividLight", "Xor" ] comp { _flag = "-compose " ++ comp; Option_edit caption labels value = this.Compose labels?value; } compose_widget = Compose "Over"; // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack coordinate_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; _flag = concat [print x.expr, ",", print y.expr]; }; Distort distort = class Option_string "Distort" [ "Affine", "AffineProjection", "ScaleRotateTranslate", "SRT", "Perspective", "PerspectiveProjection", "BilinearForward", "BilinearReverse", "Polynomial", "Arc", "Polar", "DePolar", "Barrel", "BarrelInverse", "Shepards", "Resize" ] distort { _flag = distort; Option_edit caption labels value = this.Distort labels?value; } distort_widget = Distort "SRT"; Dither dither = class Option_string "Dither" [ "None", "FloydSteinberg", "Riemersma" ] dither { _flag = "-dither " ++ dither; Option_edit caption labels value = this.Dither labels?value; } dither_widget = Dither "FloydSteinberg"; Evaluate eval = class Option_string "Evaluate operation" [ "Abs", "Add", "AddModulus", "And", "Cos", "Cosine", "Divide", "Exp", "Exponential", "GaussianNoise", "ImpulseNoise", "LaplacianNoise", "LeftShift", "Log", "Max", "Mean", "Median", "Min", "MultiplicativeNoise", "Multiply", "Or", "PoissonNoise", "Pow", "RightShift", "Set", "Sin", "Sine", "Subtract", "Sum", "Threshold", "ThresholdBlack", "ThresholdWhite", "UniformNoise", "Xor" ] eval { _flag = "-evaluate " ++ eval; Option_edit caption labels value = this.Evaluate labels?value; } evaluate_widget = Evaluate "Add"; Filter filt = class Option_string "Filter" [ "default", "Bartlett", "Blackman", "Bohman", "Box", "Catrom", "Cosine", "Cubic", "Gaussian", "Hamming", "Hann", "Hermite", "Jinc", "Kaiser", "Lagrange", "Lanczos", "Lanczos2", "Lanczos2Sharp", "LanczosRadius", "LanczosSharp", "Mitchell", "Parzen", "Point", "Quadratic", "Robidoux", "RobidouxSharp", "Sinc", "SincFast", "Spline", "Triangle", "Welch" ] filt { _flag = if filt == "default" then "" else "-filter " ++ filt; Option_edit caption labels value = this.Filter labels?value; } filter_widget = Filter "default"; Function func = class Option_string "Function" [ "Polynomial", "Sinusoid", "Arcsin", "Arctan" ] func { _flag = func; Option_edit caption labels value = this.Function labels?value; } function_widget = Function "Polynomial"; // "Polynomial (a[n], a[n-1], ... a[1], a[0])", // "Sinusoid (freq, phase, amp, bias)", // "Arcsin (width, centre, range, bias)", // "Arctan (slope, centre, range, bias)" Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; ImageType imagetype = class Option_string "Image type" [ "Bilevel", "ColorSeparation", "ColorSeparationAlpha", "ColorSeparationMatte", "Grayscale", "GrayscaleAlpha", "GrayscaleMatte", "Optimize", "Palette", "PaletteBilevelAlpha", "PaletteBilevelMatte", "PaletteAlpha", "PaletteMatte", "TrueColorAlpha", "TrueColorMatte", "TrueColor" ] imagetype { _flag = "-type " ++ imagetype; Option_edit caption labels value = this.ImageType labels?value; } imagetype_widget = ImageType "TrueColor"; Intensity intensity = class Option_string "Intensity (gray conversion)" [ "Average", "Brightness", "Lightness", "MS", "Rec601Luma", "Rec601Luminance", "Rec709Luma", "Rec709Luminance", "RMS" ] intensity { _flag = "-intensity " ++ intensity, has_intensity = ""; Option_edit caption labels value = this.Intensity labels?value; } intensity_widget = Intensity "Rec709Luminance"; Interpolate interp = class Option_string "Interpolate" [ "default", "Average", "Average4", "Average9", "Average16", "Background", "Bilinear", "Blend", "Integer", "Mesh", "Nearest", "NearestNeighbor", "Spline" ] interp { _flag = if interp == "default" then "" else "-interpolate " ++ interp; Option_edit caption labels value = this.Interpolate labels?value; } interpolate_widget = Interpolate "default"; Kernel kernel = class Option_string "Kernel" [ "Unity", "Gaussian", "DoG", "LoG", "Blur", "Comet", "Binomial", "Laplacian", "Sobel", "FreiChen", "Roberts", "Prewitt", "Compass", "Kirsch", "Diamond", "Square", "Rectangle", "Disk", "Octagon", "Plus", "Cross", "Ring", "Peaks", "Edges", "Corners", "Diagonals", "LineEnds", "LineJunctions", "Ridges", "ConvexHull", "ThinSe", "Skeleton", "Chebyshev", "Manhattan", "Octagonal", "Euclidean" // FIXME: custom kernel ] kernel { _flag = kernel; Option_edit caption labels value = this.Kernel labels?value; } kernel_widget = Kernel "Unity"; ModColSp msp = class Option_string "modulate colorspace" [ "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "LCH" ] msp { _flag = "-set option:modulate:colorspace " ++ msp; Option_edit caption labels value = this.ModColSp labels?value; } ModColSp_widget = ModColSp "HSL"; MorphMeth morph = class Option_string "Method" [ "Correlate", "Convolve", "Dilate", "Erode", "Close", "Open", "DilateIntensity", "ErodeIntensity", "CloseIntensity", "OpenIntensity", "Smooth", "EdgeOut", "EdgeIn", "Edge", "TopHat", "BottomHat", "HitAndMiss", "Thinning", "Thicken", "Distance", "IterativeDistance" ] morph { _flag = morph; Option_edit caption labels value = this.MorphMeth labels?value; } morphmeth_widget = MorphMeth "Dilate"; Noise noise = class Option_string "Noise" [ "Gaussian", "Impulse", "Laplacian", "Multiplicative", "Poisson", "Random", "Uniform" ] noise { _flag = "+noise " ++ noise; Option_edit caption labels value = this.Noise labels?value; } noise_widget = Noise "Gaussian"; Pattern pattern = class Option_string "Noise" [ // See http://www.imagemagick.org/script/formats.php "bricks", "checkerboard", "circles", "crosshatch", "crosshatch30", "crosshatch45", "gray0", "gray5", "gray10", "gray15", "gray20", "gray25", "gray30", "gray35", "gray40", "gray45", "gray50", "gray55", "gray60", "gray65", "gray70", "gray75", "gray80", "gray85", "gray90", "gray95", "gray100", "hexagons", "horizontal", "horizontal2", "horizontal3", "horizontalsaw", "hs_bdiagonal", "hs_cross", "hs_diagcross", "hs_fdiagonal", "hs_horizontal", "hs_vertical", "left30", "left45", "leftshingle", "octagons", "right30", "right45", "rightshingle", "smallfishscales", "vertical", "vertical2", "vertical3", "verticalbricks", "verticalleftshingle", "verticalrightshingle", "verticalsaw" ] pattern { _flag = "pattern:" ++ pattern; Option_edit caption labels value = this.Pattern labels?value; } pattern_widget = Pattern "bricks"; ResizeType resizet = class Option_string "Resize type" [ "resize", "scale", "sample", "adaptive-resize" ] resizet { _flag = resizet; Option_edit caption labels value = this.ResizeType labels?value; } ResizeType_widget = ResizeType "resize"; Size_widget = class { _vislevel = 3; width = Expression "Width (pixels)" 64; height = Expression "Height (pixels)" 64; _flag = "-size " ++ print width.expr ++ "x" ++ print height.expr; }; StatType statt = class Option_string "Statistic type" [ "Gradient", "Maximum", "Mean", "Median", "Minimum", "Mode", "Nonpeak", "StandardDeviation" ] statt { _flag = statt; Option_edit caption labels value = this.StatType labels?value; } StatType_widget = StatType "Mean"; VirtualPixel vp = class Option_string "Virtual pixel" [ "Background", "Black", "CheckerTile", "Dither", "Edge", "Gray", "HorizontalTile", "HorizontalTileEdge", "Mirror", "None", "Random", "Tile", "Transparent", "VerticalTile", "VerticalTileEdge", "White" ] vp { _flag = "-virtual-pixel " ++ vp; _isBackground = (vp == "Background"); Option_edit caption labels value = this.VirtualPixel labels?value; } VirtualPixel_widget = VirtualPixel "Edge"; VirtualPixelBack_widget = class { virtpix = Magick.VirtualPixel_widget; background = Magick.background_widget; _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") ++ virtpix._flag; } Geometry_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; AnnotGeometry_widget = class { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print shearX.expr, "x", print shearY.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; OffsetGeometry_widget = class { _vislevel = 3; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; WhxyGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; FrameGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; outbev = Expression "Outer bevel thickness" 0; inbev = Expression "Inner bevel thickness" 0; _flag = concat [print x.expr, "x", print y.expr, format outbev, format inbev] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } } ================================================ FILE: share/nip2/compat/8.3/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/8.3/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_tile_cache_random x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } decode im = oo_unary_function decode_op im, is_class im = decode_im im, is_image im = error (_ "bad arguments to " ++ "decode") { decode_op = Operator "decode" decode Operator_type.COMPOUND_REWRAP false; decode_im im = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK = im_rad2float im, get_coding im == Image_coding.RAD = im; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; image' = decode image; patches = map (\p extract_area p?0 p?1 sample_width sample_height image') cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } reduce kernel xshr yshr image = oo_unary_function reduce_op image, is_class image = reduce_im image, is_image image = error (_ "bad arguments to " ++ "reduce") { reduce_op = Operator "reduce" reduce_im Operator_type.COMPOUND_REWRAP false; reduce_im im = out { [out] = vips_call "reduce" [im, xshr, yshr] [$kernel => kernel.value]; } } similarity interpolate scale angle image = oo_unary_function similarity_op image, is_class image = similarity_im image, is_image image = error (_ "bad arguments to " ++ "similarity") { similarity_op = Operator "similarity" similarity_im Operator_type.COMPOUND_REWRAP false; similarity_im im = out { [out] = vips_call "similarity" [im] [ $interpolate => interpolate.value, $scale => scale, $angle => angle ]; } } resize kernel xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; is_nn = kernel.type == Kernel_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn // everything else ... we just pass on to vips_resize() = vips_resize kernel xfac' yfac' im { vips_resize kernel hscale vscale im = out { [out] = vips_call "resize" [im, hscale] [$vscale => vscale, $kernel => kernel.value]; } } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C * * Also calculate R2, see eg.: * https://en.wikipedia.org/wiki/Coefficient_of_determination */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } ss = len xes; sx = sum xes; sy = sum yes; my = sy / ss; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; my = sy / len xes; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* One image in, one out. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } /* Two images in, one out. */ system_image2 command x1 x2 = oo_binary_function system_image2_op x1 x2, is_class x1 = oo_binary'_function system_image2_op x1 x2, is_class x2 = system x1 x2, is_image x1 && is_image x2 = error (_ "bad arguments to " ++ "system_image2") { system_image2_op = Operator "system_image2" (system_image2 command) Operator_type.COMPOUND_REWRAP false; system x1 x2 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Three images in, one out. */ system_image3 command x1 x2 x3 = oo_binary_function system_image2_op x2 x3, is_class x2 = oo_binary'_function system_image2_op x2 x3, is_class x3 = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 = error (_ "bad arguments to " ++ "system_image3") { system_image2_op = Operator "system_image2" (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; system x1 x2 x3 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2, x3], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Zero images in, one out. */ system_image0 command = Image image_out { [image_out] = vips_call "system" [command] [ $out => true, $out_format => "%s.tif" ]; } hough_line w h x = oo_unary_function hough_line_op x, is_class x = hline (to_real w) (to_real h) x, is_image x = error (_ "bad arguments to " ++ "hough_line") { hough_line_op = Operator "hough_line" (hough_line w h) Operator_type.COMPOUND_REWRAP false; hline w h x = pspace { [pspace] = vips_call "hough_line" [x] [ $width => w, $height => h ]; } } hough_circle s mn mx x = oo_unary_function hough_circle_op x, is_class x = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x = error (_ "bad arguments to " ++ "hough_circle") { hough_circle_op = Operator "hough_circle" (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; hcircle s mn mx x = pspace { [pspace] = vips_call "hough_circle" [x] [ $scale => s, $min_radius => mn, $max_radius => mx ]; } } mapim interp ind in = oo_binary_function mapim_op ind in, is_class ind = oo_binary'_function mapim_op ind in, is_class in = mapim_fn ind in, is_image ind && is_image in = error (_ "bad arguments to " ++ "mapim") { mapim_op = Operator "mapim" (mapim interp) Operator_type.COMPOUND_REWRAP false; mapim_fn ind im = out { [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; } } ================================================ FILE: share/nip2/compat/8.3/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Kernel_type = class { NEAREST_NEIGHBOUR = 0; LINEAR = 1; CUBIC = 2; LANCZOS2 = 3; LANCZOS3 = 4; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map kernel numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Linear", _ "Cubic", _ "Lanczos, two lobes", _ "Lanczos, three lobes" ]; /* And to vips enum nicknames. */ types = [ "nearest", "linear", "cubic", "lanczos2", "lanczos3" ]; } Kernel type = class { value = Kernel_type.types?type; } Kernel_linear = Kernel Kernel_type.LINEAR; Kernel_picker default = class Kernel kernel.value { _vislevel = 2; kernel = Option "Kernel" Kernel_type.descriptions default; } Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; to_image dpi = extract_bands 0 3 (graph_export_image (to_real dpi) this); } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/8.4/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = print_profile, has_type image && get_type image == Image_type.CMYK && has_bands image && get_bands image >= 4 = monitor_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/8.4/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 2; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 20; fs = Scale "Sharpen flat areas by" 0 5 0.5; js = Scale "Sharpen jaggy areas by" 0 5 1; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; _result = map_unary process x { process in = hist_equalize_local window_width.expr window_height.expr in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_hough_item = class Menupullright "_Hough Transform" "transform to parameter space" { Line_item = class Menuaction "_Line" "find straight line Hough transform" { action a = class _result { _vislevel = 3; pspace_width = Expression "Parameter space width" 64; pspace_height = Expression "Parameter space height" 64; _result = map_unary line a { line a = hough_line (to_real pspace_width) (to_real pspace_height) a; } } } Circle_item = class Menuaction "_Circle" "find circle Hough transform" { action a = class _result { _vislevel = 3; scale = Expression "Scale down parameter space by" 10; min_radius = Expression "Minimum radius" 10; max_radius = Expression "Maximum radius" 30; _result = map_unary circle a { circle a = hough_circle (to_real scale) (to_real min_radius) (to_real max_radius) a; } } } } Filter_coordinate_item = class Menupullright "_Coordinate Transform" "various coordinate transforms" { // run a function which wants a complex arg on a non-complex two-band // image run_cmplx fn x = re x' ++ im x' { x' = fn (x?0, x?1); } Polar_item = class Menuaction "_Polar" "transform to polar coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_polar a { to_polar im = mapim interp.value map' im { // xy image, origin in the centre, scaled to fit image to // a circle xy = make_xy im.width im.height; xy' = xy - Vector [im.width / 2, im.height / 2]; scale = min [im.width, im.height] / im.width; xy'' = 2 * xy' / scale; // to polar, scale vertical axis to 360 degrees map = run_cmplx polar xy''; map' = map * Vector [1, im.height / 360]; } } } } Rectangular_item = class Menuaction "_Rectangular" "transform to rectangular coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_rect a { to_rect im = mapim interp.value map'' im { // xy image, vertical scaled to 360 degrees xy = make_xy im.width im.height; xy' = xy * Vector [1, 360 / im.height]; // to rect, scale to image rect map = run_cmplx rectangular xy'; scale = min [im.width, im.height] / im.width; map' = map * scale / 2; map'' = map' + Vector [im.width / 2, im.height / 2]; } } } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "_Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } Autotrace_item = class Menuaction "_Trace" "convert a bitmap to an SVG file" { action x = class _result { _vislevel = 3; despeckle = Scale "Despeckle level" 1 20 1; line = Scale "Line threshold" 1 20 1; center = Toggle "Trace centreline" false; scale = Scale "SVG scale" 0.1 10 1; command = "autotrace %s " ++ join_sep " " [ofmt, ofile, desp, lint, cent] { prog = search_for_error "autotrace"; ofmt = "-output-format svg"; ofile = "-output-file %s"; desp = "-despeckle-level " ++ print despeckle.value; lint = "-line-threshold " ++ print line.value; cent = if center then "-centerline " else ""; } _result = Image output { [output] = vips_call "system" [command] [$in => [x.value], $in_format => "%s.ppm", $out => true, $out_format => "%s.svg[scale=" ++ print scale.value ++ "]" ]; } } } ================================================ FILE: share/nip2/compat/8.4/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Kernel_linear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; caption = Expression "Chart caption" "none"; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; xcaption = Expression "X axis caption" "none"; ycaption = Expression "Y axis caption" "none"; series_captions = Expression "Series captions" ["Band 0"]; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range ++ captions; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; captions = concat (map test caption_options) ++ [$series_captions => series_captions.expr] { caption_options = [ $caption => caption.expr, $xcaption => xcaption.expr, $ycaption => ycaption.expr ]; test x = [], value == "none" = [option_name => value] { [option_name, value] = x; } } image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/8.4/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel h v image, aspect = resize kernel fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize kernel fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_map_item = class Menuaction "_Map" "map an image through a 2D transform image" { action a b = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_binary trans a b { trans a b = mapim interp.value in index { // get the index image first [index, in] = sortc (const is_twocomponent) [a, b]; // is a two-component image, ie. one band complex, or // two-band non-complex is_twocomponent x = is_nonc x || is_c x; is_nonc x = has_bands x && get_bands x == 2 && has_format x && !is_complex_format (get_format x); is_c x = has_bands x && get_bands x == 1 && has_format x && is_complex_format (get_format x); is_complex_format f = f == Image_format.COMPLEX || f == Image_format.DPCOMPLEX; } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 100; sy = Expression "Start y" 100; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Noise_item = class Menupullright "_Noise" "various noise generators" { Gaussian_item = class Menuaction "_Gaussian" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal noise image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Perlin_item = class Menuaction "_Perlin" "Perlin noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; cell_size = Expression "Cell size (pixels)" 8; eight = Toggle "Eight bit output" true; _result = 128 * im + 128, eight = im { im = perlin cell_size nwidth nheight; } } } Worley_item = class Menuaction "_Worley" "Worley noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 512; nheight = Expression "Image height (pixels)" 512; cell_size = Expression "Cell size (pixels)" 256; _result = worley cell_size nwidth nheight; } } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/8.4/Magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate Magick.def 13-Apr-2014 snibgo Put "new image" items into sub-menu. New class VirtualPixlBack. 17-Apr-2014 snibgo Many small changes. A few new menu options. Created sub-menu for multi-input operations. 3-May-2014 jcupitt Put quotes around ( in shadow to help unix Last update: 17-Apr-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ // We don't need Noop. /*=== Magick_noop_item = class Menuaction "_Noop" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_testPar_item = class Menuaction "_TestPar" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "( +clone ) +append ", "\"%s\"" ]; _result = Magick.system command x; } } /* Removed Read_item and Write_item, much better to use nip2 load/save image. * Plus they can load all libMagick formats anyway. */ // Put "new image" items into sub-menu Magick_NewImageMenu_item = class Menupullright "_New image" "make a new image" { Magick_newcanvas_item = class Menuaction "_Solid colour" "make image of solid colour" { action = class _result { _vislevel = 3; size = Magick.Size_widget; colour = Magick.generalcol_widget; command = Magick.command [ size._flag, "\"canvas:" ++ colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_builtin_item = class Menuaction "_Built-in image" "create a built-in image" { action = class _result { _vislevel = 3; builtin = Magick.builtin_widget; command = Magick.command [ builtin._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_gradient_item = class Menuaction "_Gradient" "make a linear gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; topColour = Magick.generalcol_widget; bottomColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"gradient:", topColour._flag, "-", bottomColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } Magick_hald_item = class Menuaction "_Hald-clut image" "create an identity hald-clut image" { action = class _result { _vislevel = 3; order = Expression "order" 8; command = Magick.command [ "hald:" ++ print order.expr, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_pattern_item = class Menuaction "_Pattern" "create pattern" { action = class _result { _vislevel = 3; size = Magick.Size_widget; pattern = Magick.pattern_widget; command = Magick.command [ size._flag, pattern._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_plasma_item = class Menuaction "_Plasma image" "create plasma image" { action = class _result { _vislevel = 3; size = Magick.Size_widget; // FIXME? ColourA-ColourB. // FIXME? Allow plasma:fractal? command = Magick.command [ size._flag, "plasma:", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_radialgradient_item = class Menuaction "_Radial gradient" "make a radial gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; innerColour = Magick.generalcol_widget; outerColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"radial-gradient:", innerColour._flag, "-", outerColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } } // end Magick_NewImageMenu_item Magick_MultiMenu_item = class Menupullright "_Multiple inputs" "make an image from multiple images" { Magick_composite_item = class Menuaction "_Composite" "composite two images (without mask)" { action x y = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_compositeMask_item = class Menuaction "_Composite masked" "composite two images (with mask)" { action x y z = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system3 command x y z; } } // FIXME: other operations like remap that take another image as arguments are: // mask (pointless?), texture, tile (pointless?) // FIXME: operations that take a filename that isn't an image: // cdl, profile Magick_clut_item = class Menuaction "_Clut" "replace values using second image as colour look-up table" { action x y = class _result { _vislevel = 3; // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" command = Magick.command [ "\"%s\"", "\"%s\"", "-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_haldclut_item = class Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"", "-hald-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } // Encipher and decipher: key files can be text or image files. Magick_encipher_item = class Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { action x = class _result { _vislevel = 3; pathname = Pathname "Read key file" ""; isDecipher = Toggle "Decipher" false; command = Magick.command [ "\"%s\"", if pathname.value == "" then "" else ( ( if isDecipher then "-decipher " else "-encipher ") ++ ( "\"" ++ pathname.value ++ "\"" ) ), "\"%s\"" ]; _result = Magick.system command x; } } Magick_profile_item = class Menuaction "_Profile" "assigns/applies an ICC profile" { action x = class _result { _vislevel = 3; pathname1 = Pathname "Read profile file" ""; pathname2 = Pathname "Read profile file" ""; command = Magick.command [ "\"%s\"", if pathname1.value == "" then "" else ( "-profile " ++ "\"" ++ pathname1.value ++ "\""), if pathname2.value == "" then "" else ( "-profile " ++ "\"" ++ pathname2.value ++ "\""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_remap_item = class Menuaction "_Remap" "reduce colours to those in another image" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-remap", "\"%s\"", "\"%s\"" ]; _result = Magick.system2 command x y; } } } // end Magick_MultiMenu_item Magick_image_type_item = class Menuaction "_Image Type" "change image type" { action x = class _result { _vislevel = 3; imagetype = Magick.imagetype_widget; command = Magick.command [ "\"%s\"", imagetype._flag, "\"%s\"" ]; _result = Magick.system command x; } } sep2 = Menuseparator; Magick_alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = Magick.alpha_widget; command = Magick.command [ "\"%s\"", alpha._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = Magick.text_widget; font = Magick.Font_widget; geometry = Magick.AnnotGeometry_widget; gravity = Magick.gravity_widget; foreground = Magick.foreground_widget; undercol = Magick.undercol_widget; antialias = Magick.antialias_widget; command = Magick.command [ "\"%s\"", font._flag, antialias._flag, gravity._flag, foreground._flag, undercol._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoGamma_item = class Menuaction "_AutoGamma" "automatic gamma" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-gamma", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoLevel_item = class Menuaction "_AutoLevel" "automatic level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-level", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blurSharpMenu_item = class Menupullright "_Blur/Sharpen" "blur and sharpen" { Magick_adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; // note: adaptive-blur doesn't regard VP. command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-adaptive-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_blur_item = class Menuaction "_Blur" "blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gaussianBlur_item = class Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-gaussian-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_motionBlur_item = class Menuaction "_Motion Blur" "simulate motion blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; channels = Magick.ch_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-motion-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotationalBlur_item = class Menuaction "_RotationalBlur" "blur around the centre" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; angle = Scale "angle (degrees)" (-360) 360 20; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-radial-blur", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_selectiveBlur_item = class Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; threshold = Scale "Threshold (percent)" 0 100 50; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", intensity._flag, virtpixback._flag, channels._flag, "-selective-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print threshold.value ++ "%%", "\"%s\"" ]; _result = Magick.system command x; } } sep1 = Menuseparator; Magick_adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-adaptive-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sharpen_item = class Menuaction "_Sharpen" "sharpen" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_unsharpen_item = class Menuaction "_Unsharp" "sharpen with unsharp mask" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; gain = Scale "Gain" (-10) 10 1; threshold = Scale "Threshold" 0 1 0.05; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-unsharp", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print gain.value ++ "+" ++ print threshold.value, "\"%s\"" ]; _result = Magick.system command x; } } } // end BlurSharpMenu_item Magick_border_item = class Menuaction "_Border" "add border of given colour" { action x = class _result { _vislevel = 3; compose = Magick.compose_widget; width = Expression "Width" 3; bordercol = Magick.bordercol_widget; command = Magick.command [ "\"%s\"", compose._flag, bordercol._flag, "-border", print width.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_brightCont_item = class Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { action x = class _result { _vislevel = 3; bri = Scale "brightness" (-100) 100 0; con = Scale "contrast" (-100) 100 0; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-brightness-contrast", print bri.value ++ "x" ++ print con.value, "\"%s\"" ]; _result = Magick.system command x; } } // Note: canny requires ImageMagick 6.8.9-0 or later. Magick_canny_item = class Menuaction "_Canny" "detect a wide range of edges" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; lowPc = Scale "lower percent" 0 100 10; highPc = Scale "lower percent" 0 100 10; command = Magick.command [ "\"%s\"", "-canny", concat ["\"", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print lowPc.value ++ "%%+" ++ print highPc.value ++ "%%" ++ "\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_charcoal_item = class Menuaction "_Charcoal" "simulate a charcoal drawing" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; factor = Scale "factor" 0 50 1; command = Magick.command [ "\"%s\"", intensity._flag, "-charcoal", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_chop_item = class Menuaction "_Chop" "remove pixels from the interior" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-chop", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorize_item = class Menuaction "_Colorize" "colorize by given amount" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; val = Scale "value" 0 100 100; command = Magick.command [ "\"%s\"", foreground._flag, "-colorize", print val.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colors_item = class Menuaction "_Colors" "reduce number of colors" { action x = class _result { _vislevel = 3; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; quantize = Magick.colorspace_widget; colors = Expression "Colours" 3; command = Magick.command [ "\"%s\"", "-quantize", quantize._flag, "-treedepth", print treedepth.expr, dither._flag, "-colors", print colors.expr, "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: color-matrix? Magick_colorspace_item = class Menuaction "_Colourspace" "convert to arbitrary colourspace" { action x = class _result { _vislevel = 3; colsp = Magick.colorspace_widget; command = Magick.command [ "\"%s\"", "-colorspace", colsp._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorspaceGray_item = class Menuaction "_Colourspace gray" "convert to gray using given intensity method" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-colorspace gray", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrast_item = class Menuaction "_Contrast" "increase or reduce the contrast" { action x = class _result { _vislevel = 3; isReduce = Toggle "reduce contrast" false; command = Magick.command [ "\"%s\"", (if isReduce then "+" else "-") ++ "contrast", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrastStretch_item = class Menuaction "_Contrast stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-contrast-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: convolve (bias, kernel) Magick_crop_item = class Menuaction "_Crop" "cut out a rectangular region" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-crop", geometry._flag, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_deskew_item = class Menuaction "_Deskew" "straighten the image" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; // FIXME: toggle auto-crop? command = Magick.command [ "\"%s\"", "-deskew", "\"" ++ print threshold.value ++ "%%\"", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_despeckle_item = class Menuaction "_Despeckle" "reduce the speckles" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-despeckle", "\"%s\"" ]; _result = Magick.system command x; } } Magick_distort_item = class Menuaction "_Distort" "distort using a method and arguments" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; distort = Magick.distort_widget; args = String "Arguments" "1,0"; isPlus = Toggle "Extend to show entire image" false; command = Magick.command [ "\"%s\"", virtpixback._flag, (if isPlus then "+" else "-") ++ "distort", distort._flag, args.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_draw_item = class Menuaction "_Draw" "annotate with one or more graphic primitives" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; command = Magick.command [ "\"%s\"", foreground._flag, "-draw", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_edge_item = class Menuaction "_Edge" "detect edges" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-edge", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_emboss_item = class Menuaction "_Emboss" "emboss" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-emboss", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_enhance_item = class Menuaction "_Enhance" "enhance a noisy image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-enhance", "\"%s\"" ]; _result = Magick.system command x; } } Magick_equalize_item = class Menuaction "_Equalize" "equalize the histogram" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-equalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_evaluate_item = class Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; operation = Magick.evaluate_widget; val = Expression "value" 5; isPc = Toggle "Value is percent" false; command = Magick.command [ "\"%s\"", channels._flag, operation._flag, print val.expr ++ if isPc then "%%" else "", "\"%s\"" ]; _result = Magick.system command x; } } Magick_extent_item = class Menuaction "_Extent" "set the image size and offset" { action x = class _result { _vislevel = 3; background = Magick.background_widget; compose = Magick.compose_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, background._flag, gravity._flag, "-extent", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_FlipFlopMenu_item = class Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { Magick_flip_item = class Menuaction "_Flip vertically" "mirror upside-down" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flip", "\"%s\"" ]; _result = Magick.system command x; } } Magick_flop_item = class Menuaction "_Flop horizontally" "mirror left-right" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flop", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transpose_item = class Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transpose +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transverse_item = class Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transverse +repage", "\"%s\"" ]; _result = Magick.system command x; } } } // end Magick_FlipFlopMenu_item Magick_floodfill_item = class Menuaction "_Floodfill" "recolour neighbours that match" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; fuzz = Magick.fuzz_widget; coordinate = Magick.coordinate_widget; // -draw "color x,y floodfill" command = Magick.command [ "\"%s\"", foreground._flag, "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-draw \" color", coordinate._flag, "floodfill \"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_frame_item = class Menuaction "_Frame" "surround with border or beveled frame" { action x = class _result { _vislevel = 3; border = Magick.bordercol_widget; compose = Magick.compose_widget; matte = Magick.mattecol_widget; geometry = Magick.FrameGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, border._flag, matte._flag, "-frame", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_function_item = class Menuaction "_Function" "evaluate a function on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; function = Magick.function_widget; // FIXME: explain values; use sensible defaults. values = String "values" "0,0,0,0"; command = Magick.command [ "\"%s\"", channels._flag, "-function", function._flag, values.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_fx_item = class Menuaction "_Fx" "apply a mathematical expression" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; interpolate = Magick.interpolate_widget; virtpixback = Magick.VirtualPixelBack_widget; args = String "Expression" "u*1/2"; command = Magick.command [ "\"%s\"", channels._flag, interpolate._flag, virtpixback._flag, "-fx", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_gamma_item = class Menuaction "_Gamma" "apply a gamma correction" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; gamma = Magick.gamma_widget; command = Magick.command [ "\"%s\"", channels._flag, "-gamma", print gamma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradient_item = class Menuaction "_Gradient" "apply a linear gradient" { action x = class _result { _vislevel = 3; colourA = Magick.generalcol_widget; colourB = Magick.generalcol_widget; position = Option "colourA is at" [ "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"] 0; _baryArg = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 = "dunno"; command = Magick.command [ "\"%s\"", "-sparse-color barycentric \"" ++ _baryArg ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradientCorn_item = class Menuaction "_Gradient corners" "apply a bilinear gradient between the corners" { action x = class _result { _vislevel = 3; colour_top_left = Magick.generalcol_widget; colour_top_right = Magick.generalcol_widget; colour_bottom_left = Magick.generalcol_widget; colour_bottom_right = Magick.generalcol_widget; command = Magick.command [ "\"%s\"", "-sparse-color bilinear \"" ++ "0,0," ++ colour_top_left._flag ++ ",%%[fx:w-1],0" ++ colour_top_right._flag ++ ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", "+depth", "\"%s\"" ]; _result = Magick.system command x; } } Magick_histogram_item = class Menuaction "_Histogram" "make a histogram image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-define histogram:unique-colors=false histogram:" ++ "\"%s\"" ]; _result = Magick.system command x; } } Magick_implode_item = class Menuaction "_Implode" "implode pixels about the center" { action x = class _result { _vislevel = 3; factor = Scale "factor" 0 20 1; interpolate = Magick.interpolate_widget; // FIXME: virtual-pixel? command = Magick.command [ "\"%s\"", interpolate._flag, "-implode", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_level_item = class Menuaction "_Level" "adjust the level of channels" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "black point" (-100) 200 0; wht = Scale "white point" (-100) 200 100; gam = Scale "gamma" 0 30 1; isPc = Toggle "Levels are percent" true; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level", "\"" ++ print blk.value ++ "," ++ print wht.value ++ (if isPc then "%%" else "") ++ "," ++ print gam.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_levelCols_item = class Menuaction "_Level colors" "adjust levels to given colours" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; colour_black = Magick.generalcol_widget; colour_white = Magick.generalcol_widget; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level-colors", "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_linearStretch_item = class Menuaction "_Linear stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", channels._flag, "-linear-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_magnify_item = class Menuaction "_Magnify" "double the size of the image with pixel art scaling" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-magnify", "\"%s\"" ]; _result = Magick.system command x; } } Magick_modulate_item = class Menuaction "_Modulate" "modulate brightness, saturation and hue" { action x = class _result { _vislevel = 3; modcolsp = Magick.ModColSp_widget; bright = Scale "brightness" 0 200 100; sat = Scale "saturation" 0 200 100; hue = Scale "hue" 0 200 100; command = Magick.command [ "\"%s\"", modcolsp._flag, "-modulate", print bright.value ++ "," ++ print sat.value ++ "," ++ print hue.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_monochrome_item = class Menuaction "_Monochrome" "transform to black and white" { action x = class _result { _vislevel = 3; // FIXME: also intensity? intensity = Magick.intensity_widget; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; command = Magick.command [ "\"%s\"", intensity._flag, dither._flag, "-treedepth", print treedepth.expr, "-monochrome", "\"%s\"" ]; _result = Magick.system command x; } } Magick_morphology_item = class // See http://www.imagemagick.org/Usage/morphology/ Menuaction "_Morphology" "apply a morphological method" { action x = class _result { _vislevel = 3; method = Magick.morphmeth_widget; iter = Expression "Iterations (-1=repeat until done)" 1; kernel = Magick.kernel_widget; // FIXME: custom kernel eg "3x1+2+0:1,0,0" // width x height + offsx + offsy : {w*h values} // each value is 0.0 to 1.0 or "NaN" or "-" // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 // but // ring takes: radius1, radius2, scale // rectangle and comet take: width x height + offsx + offsy // blur takes: radius x sigma // FIXME: for now, simply allow any string input. kernel_arg = String "Kernel arguments" ""; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-morphology", method._flag ++ ":" ++ print iter.expr, kernel._flag ++ (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_negate_item = class Menuaction "_Negate" "negate" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-negate", "\"%s\"" ]; _result = Magick.system command x; } } Magick_addNoise_item = class Menuaction "_add Noise" "add noise" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; attenuate = Scale "attenuate" 0 1.0 1.0; noise = Magick.noise_widget; command = Magick.command [ "\"%s\"", channels._flag, "-attenuate", print attenuate.value, noise._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_normalize_item = class Menuaction "_Normalize" "normalize" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-normalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_opaque_item = class Menuaction "_Opaque" "change this colour to the fill colour" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; fill = Magick.foreground_widget; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", fill._flag, channels._flag, "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "opaque", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_paint_item = class Menuaction "_Paint" "simulate an oil painting" { action x = class _result { _vislevel = 3; rad = Expression "radius" 10; command = Magick.command [ "\"%s\"", "-paint", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } /*=== FIXME Bug; remove for now. Polaroid_item = class Menuaction "_Polaroid" "simulate a polaroid picture" { action x = class _result { _vislevel = 3; angle = Scale "angle" (-90) 90 20; command = Magick.command [ "\"%s\"", "-polaroid", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_posterize_item = class Menuaction "_Posterize" "reduce to (n) levels per channel" { action x = class _result { _vislevel = 3; levels = Expression "levels" 3; command = Magick.command [ "\"%s\"", "-posterize", print levels.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_raise_item = class Menuaction "_Raise" "lighten or darken image edges" { action x = class _result { _vislevel = 3; thk = Expression "Thickness" 3; command = Magick.command [ "\"%s\"", "-raise", print thk.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_resize_item = class Menuaction "_Resize" "resize to given width and height" { action x = class _result { _vislevel = 3; filter = Magick.filter_widget; type = Magick.ResizeType_widget; width = Scale "Width" 1 100 10; height = Scale "Height" 1 100 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", filter._flag, "-" ++ type._flag, "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "!") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_roll_item = class Menuaction "_Roll" "roll an image horizontally or vertically" { action x = class _result { _vislevel = 3; rollx = Expression "X" 3; rolly = Expression "Y" 3; command = Magick.command [ "\"%s\"", "-roll", (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotate_item = class Menuaction "_Rotate" "rotate" { action x = class _result { _vislevel = 3; angle = Scale "angle (degrees)" (-360) 360 20; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, "+distort", "SRT", print angle.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -segment, but cluster-threshold should be percentage of image area. Magick_sepia_item = class Menuaction "_Sepia tone" "simulate a sepia-toned photo" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; command = Magick.command [ "\"%s\"", "-sepia-tone", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shade_item = class Menuaction "_Shade" "shade with a distant light source" { action x = class _result { _vislevel = 3; azimuth = Scale "Azimuth (degrees)" (-360) 360 0; elevation = Scale "Elevation (degrees)" 0 90 45; command = Magick.command [ "\"%s\"", "-shade", print azimuth.value ++ "x" ++ print elevation.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_shadow_item = class Menuaction "_Shadow" "simulate a shadow" { action x = class _result { _vislevel = 3; shadowCol = Magick.generalcol_widget; opacity = Scale "Opacity (percent)" 0 100 75; sigma = Scale "Sigma" 0 30 2; // FIXME: make offsets a single widget? offsx = Scale "X-offset" (-20) 20 4; offsy = Scale "Y-offset" (-20) 20 4; arePc = Toggle "offsets are percentages" false; // FIXME: raw operation creates page offset, which vips dislikes. // So we take this futher, compositing with source. command = Magick.command [ "\"%s\"", "\"(\" +clone", "-background", "\"" ++ shadowCol._flag ++ "\"", "-shadow", concat [ "\"", print opacity.value, "x", print sigma.value, (if offsx.value >= 0 then "+" else ""), print offsx.value, (if offsy.value >= 0 then "+" else ""), print offsy.value, (if arePc then "%%" else ""), "\"" ], "\")\" +swap -background None -layers merge", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shave_item = class Menuaction "_Shave" "shave pixels from the edges" { action x = class _result { _vislevel = 3; width = Scale "Width" 0 50 10; height = Scale "Height" 0 50 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", "-shave", "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shear_item = class Menuaction "_Shear" "shear along the x-axis and/or y-axis" { action x = class _result { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-shear", print shearX.expr ++ "x" ++ print shearY.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sigmoid_item = class Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; contrast = Scale "contrast" 0 30 3; midpoint = Scale "mid-point (percent)" 0 100 50; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "sigmoidal-contrast", "\"" ++ print contrast.value ++ "x" ++ print midpoint.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_sketch_item = class Menuaction "_Sketch" "simulate a pencil sketch" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; command = Magick.command [ "\"%s\"", "-sketch", print radius.value ++ "x" ++ print sigma.value ++ (if angle >= 0 then ("+" ++ print angle.value) else ""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_solarize_item = class Menuaction "_Solarize" "negate all pixels above a threshold level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-solarize", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -sparse-color needs abitrary list of {x,y,colour}. Magick_splice_item = class Menuaction "_Splice" "splice a colour into the image" { action x = class _result { _vislevel = 3; background = Magick.background_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", background._flag, gravity._flag, "-splice", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_spread_item = class Menuaction "_Spread" "displace pixels by random amount" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; amount = Expression "Amount (pixels)" 5; command = Magick.command [ "\"%s\"", virtpixback._flag, "-spread", print amount.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_statistic_item = class Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { action x = class _result { _vislevel = 3; width = Expression "Width" 5; height = Expression "Height" 5; statisticType = Magick.StatType_widget; command = Magick.command [ "\"%s\"", "-statistic", statisticType._flag, print width.expr ++ "x" ++ print height.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = Magick.angle_widget; command = Magick.command [ "\"%s\"", "-swirl", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_thresholdMenu_item = class Menupullright "_Threshold" "make black or white" { Magick_threshold_item = class Menuaction "_Threshold" "apply black/white threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blackThreshold_item = class Menuaction "_Black threshold" "where below threshold set to black" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-black-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_whiteThreshold_item = class Menuaction "_White threshold" "where above threshold set to white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-white-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_latThreshold_item = class Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { action x = class _result { _vislevel = 3; width = Expression "Width" 10; height = Expression "Height" 10; offset = Scale "Offset (percent)" (-100) 100 0; // note: "-lat" doesn't respond to channels command = Magick.command [ "\"%s\"", "-lat", concat ["\"", print width.expr, "x", print height.expr, (if offset.value >= 0 then "+" else ""), print offset.value, "%%\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_randThreshold_item = class Menuaction "_Random Threshold" "between specified limits, apply random threshold" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; low = Scale "Low threshold" 0 100 10; high = Scale "High threshold" 0 100 90; isPc = Toggle "Thresholds are percent" true; command = Magick.command [ "\"%s\"", channels._flag, "-random-threshold", "\"" ++ print low.value ++ "x" ++ print high.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } } // end ThresholdMenu_item // Note: alternatives include: // convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif Magick_tile_item = class Menuaction "_Tile" "fill given size with tiled image" { action x = class _result { _vislevel = 3; size = Magick.Size_widget; command = Magick.command [ size._flag, "tile:" ++ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_tint_item = class Menuaction "_Tint" "apply a tint" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; amount = Scale "amount (percent)" 0 100 50; command = Magick.command [ "\"%s\"", foreground._flag, "-tint", // snibgo note: although the amount is a percentage, it doesn't need "%" character. print amount.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_transparent_item = class Menuaction "_Transparent" "make this colour transparent" { action x = class _result { _vislevel = 3; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "transparent", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_trim_item = class Menuaction "_Trim" "trims away border" { action x = class _result { _vislevel = 3; fuzz = Magick.fuzz_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-trim +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_uniqueCols_item = class Menuaction "_Unique colours" "discard all but one of any pixel color" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-unique-colors", "\"%s\"" ]; _result = Magick.system command x; } } Magick_vignette_item = class Menuaction "_Vignette" "soften the edges in vignette style" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; rx = Scale "Rolloff x (percent)" 0 100 10; ry = Scale "Rolloff y (percent)" 0 100 10; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-vignette", print radius.value ++ "x" ++ print sigma.value ++ (if rx.value >= 0 then "+" else "") ++ print rx.value ++ (if ry.value >= 0 then "+" else "") ++ print ry.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_wave_item = class Menuaction "_Wave" "shear the columns into a sine wave" { action x = class _result { _vislevel = 3; amplitude = Scale "Amplitude (pixels)" 0 100 10; wavelength = Scale "Wavelength (pixels)" 0 100 10; command = Magick.command [ "\"%s\"", "-wave", print amplitude.value ++ "x" ++ print wavelength.value, "\"%s\"" ]; _result = Magick.system command x; } } ================================================ FILE: share/nip2/compat/8.4/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/8.4 start_DATA = \ Math.def \ Image.def \ Magick.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ Preferences.ws \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _magick.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/8.4/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/8.4/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/8.4/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/8.4/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/8.4/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Kernel_linear xfactor yfactor scale_im; _offset_im = resize Kernel_linear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/8.4/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/8.4/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/8.4/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart > 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [RGB16, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; /* Return $PATH, reformatted as [["comp1", "comp2"], ["comp1", "comp2"] ..] */ system_search_path = [vipsbin] ++ map path_parse (split (equal path_sep) (expand "$PATH")) { /* On some platforms we ship vips with a few extra progs. Search * $VIPSHOME/bin first. */ vipsbin = path_parse (expand "$VIPSHOME") ++ ["bin"]; path_sep = ':', expand "$SEP" == "/" = ';'; } /* Search $PATH for the first occurence of name, or "". */ search_for name = hits?0, hits != [] = "" { exe_name = name ++ expand "$EXEEXT"; form_path p = path_absolute (p ++ [exe_name]); paths = map form_path system_search_path; hits = dropwhile (equal []) (map search paths); } /* Search $PATH for the first occurence of name, error on failure. */ search_for_error name = path, path != "" = error (exe_name ++ " not found on your search path. " ++ "Check you have installed the program and it is on your PATH.") { exe_name = name ++ expand "$EXEEXT"; path = search_for name; } ================================================ FILE: share/nip2/compat/8.4/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/compat/8.4/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Kernel_linear f1 1 b1 {b1 = resize Kernel_linear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/8.4/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/8.4/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/8.4/_magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate _magick.def Add 0-ary and 2-ary system Put utility funcs into a Magick class 11-Apr-2014 snibgo Added VirtualPixelBack for cases where background is only relevant when VP=Background 17-Apr-2014 snibgo Many small changes. 2-May-2014 jcupitt Added Magick.version 30-June-2014 Put single-quotes around command exe to help win 1-July-2014 Automatically fall back to gm if we can't find convert 17-July-2014 better GM support Last update: 17-July-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ /* Put these in a class to avoid filling the main namespace with IM stuff. */ Magick = class { // first gm on path, or "" gm_path = search_for "gm"; // first convert on $PATH, or "" // we check for the convert we ship first convert_path = vips_convert, vips_convert != "" = search_for "convert" { // the convert we ship with the vips binary on some platforms, or "" vips_convert = search (path_absolute convert) { vipshome = path_parse (expand "$VIPSHOME"); convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; } } use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; // Are we in GM or IM mode? use_gm = true, use_gm_pref && gm_path != "" = false, !use_gm_pref && convert_path != "" = false, convert_path != "" = true, gm_path != "" = error "neither IM nor GM executable found"; command_path = gm_path, use_gm = convert_path; // try to get the version as eg. [6, 7, 7, 10] // GM versions are smaller, typically [1, 3, 18] version = map parse_int (split (member ".-") version_string) { [output] = vips_call "system" ["'" ++ command_path ++ "' -version"] [$log=>true]; version_string = (split (equal ' ') output)?1, use_gm = (split (equal ' ') output)?2; } // make a command-line ... args is a [str] we join with spaces command args = "'" ++ command_path ++ "' " ++ join_sep " " args' { args' = ["convert"] ++ args, use_gm = args; } // capabilities ... different versions support different features, we // turn features on and off based on these // would probably be better to test for caps somehow has_intensity = false, use_gm = version?0 > 6 || version?1 > 7; has_channel = false, use_gm = version?0 > 6 || version?1 > 7; system0 cmd = system_image0 cmd; system cmd x = map_unary (system_image cmd) x; system2 cmd x y = map_binary (system_image2 cmd) x y; system3 cmd x y z = map_trinary (system_image3 cmd) x y z; radius_widget = Scale "Radius" 0 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle (degrees)" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; gamma_widget = Scale "Gamma" 0 10 1; colors_widget = Scale "Colors" 1 10 3; resize_widget = Scale "Resize (percent)" 0 500 100; fuzz_widget = Scale "Fuzz (percent)" 0 100 0; blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; // a colour with no enclosing quotes ... use this if we know there are // some quotes at an outer level print_colour_nq triple = concat ["#", concat (map fmt triple)] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } // we need the quotes because # is the comment character in *nix print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; GeneralCol triple = class Colour "sRGB" triple { _flag = print_colour_nq triple; Colour_edit space triple = this.GeneralCol triple; } generalcol_widget = GeneralCol [0, 0, 0]; Background triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" false; _flag = "-background " ++ if isNone then "None" else print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Bordercol triple = class Colour "sRGB" triple { _flag = "-bordercolor " ++ print_colour triple; Colour_edit space triple = this.Bordercol triple; } bordercol_widget = Bordercol [0, 0, 0]; Mattecol triple = class Colour "sRGB" triple { _flag = "-mattecolor " ++ print_colour triple; Colour_edit space triple = this.Mattecol triple; } mattecol_widget = Mattecol [189, 189, 189]; // FIXME: Undercolour, like many others, can have alpha channel. // How does user input this? With a slider? Undercol triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" true; _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); Colour_edit space triple = this.Undercol triple; } undercol_widget = Undercol [0, 0, 0]; changeCol_widget = class { _vislevel = 3; colour = GeneralCol [0, 0, 0]; fuzz = fuzz_widget; nonMatch = Toggle "change non-matching colours" false; } Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Remove", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Builtin builtin = class Option_string "Builtin" [ // See http://www.imagemagick.org/script/formats.php "rose:", "logo:", "wizard:", "granite:", "netscape:" ] builtin { _flag = builtin; Option_edit caption labels value = this.Builtin labels?value; } builtin_widget = Builtin "rose:"; channels_widget = class { // FIXME? Can we grey-out alpha when we have no alpha channel, // show CMY(K) instead of RGB(K) etc? // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. ChanR valueR = class Toggle "Red" valueR { _flag = "R", valueR = ""; Toggle_edit caption valueR = this.ChanR valueR; } channelR = ChanR true; ChanG valueG = class Toggle "Green" valueG { _flag = "G", valueG = ""; Toggle_edit caption valueG = this.ChanG valueG; } channelG = ChanG true; ChanB valueB = class Toggle "Blue" valueB { _flag = "B", valueB = ""; Toggle_edit caption valueB = this.ChanB valueB; } channelB = ChanB true; ChanK valueK = class Toggle "Black" valueK { _flag = "K", valueK = ""; Toggle_edit caption valueK = this.ChanK valueK; } channelK = ChanK true; ChanA valueA = class Toggle "Alpha" valueA { _flag = "A", valueA = ""; Toggle_edit caption valueA = this.ChanA valueA; } channelA = ChanA false; ChanSy valueSy = class Toggle "Sync" valueSy { _flag = ",sync", valueSy = ""; Toggle_edit caption valueSy = this.ChanSy valueSy; } channelSy = ChanSy true; _rgbka = concat [channelR._flag, channelG._flag, channelB._flag, channelK._flag, channelA._flag ]; _flag = "", _rgbka == "" || !has_channel = concat [ "-channel ", _rgbka, channelSy._flag ]; } ch_widget = channels_widget; Colorspace colsp = class Option_string "Colorspace" [ "CIELab", "CMY", "CMYK", "Gray", "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "Lab", "LCH", "LCHab", "LCHuv", "LMS", "Log", "Luv", "OHTA", "Rec601Luma", "Rec601YCbCr", "Rec709Luma", "Rec709YCbCr", "RGB", "scRGB", "sRGB", "Transparent", "XYZ", "YCbCr", "YDbDr", "YCC", "YIQ", "YPbPr", "YUV" ] colsp { _flag = colsp; Option_edit caption labels value = this.Colorspace labels?value; } colorspace_widget = Colorspace "sRGB"; Compose comp = class Option_string "Compose method" [ "Atop", "Blend", "Blur", "Bumpmap", "ChangeMask", "Clear", "ColorBurn", "ColorDodge", "Colorize", "CopyBlack", "CopyBlue", "CopyCyan", "CopyGreen", "Copy", "CopyMagenta", "CopyOpacity", "CopyRed", "CopyYellow", "Darken", "DarkenIntensity", "DivideDst", "DivideSrc", "Dst", "Difference", "Displace", "Dissolve", "Distort", "DstAtop", "DstIn", "DstOut", "DstOver", "Exclusion", "HardLight", "Hue", "In", "Lighten", "LightenIntensity", "LinearBurn", "LinearDodge", "LinearLight", "Luminize", "Mathematics", "MinusDst", "MinusSrc", "Modulate", "ModulusAdd", "ModulusSubtract", "Multiply", "None", "Out", "Overlay", "Over", "PegtopLight", "PinLight", "Plus", "Replace", "Saturate", "Screen", "SoftLight", "Src", "SrcAtop", "SrcIn", "SrcOut", "SrcOver", "VividLight", "Xor" ] comp { _flag = "-compose " ++ comp; Option_edit caption labels value = this.Compose labels?value; } compose_widget = Compose "Over"; // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack coordinate_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; _flag = concat [print x.expr, ",", print y.expr]; }; Distort distort = class Option_string "Distort" [ "Affine", "AffineProjection", "ScaleRotateTranslate", "SRT", "Perspective", "PerspectiveProjection", "BilinearForward", "BilinearReverse", "Polynomial", "Arc", "Polar", "DePolar", "Barrel", "BarrelInverse", "Shepards", "Resize" ] distort { _flag = distort; Option_edit caption labels value = this.Distort labels?value; } distort_widget = Distort "SRT"; Dither dither = class Option_string "Dither" [ "None", "FloydSteinberg", "Riemersma" ] dither { _flag = "-dither " ++ dither; Option_edit caption labels value = this.Dither labels?value; } dither_widget = Dither "FloydSteinberg"; Evaluate eval = class Option_string "Evaluate operation" [ "Abs", "Add", "AddModulus", "And", "Cos", "Cosine", "Divide", "Exp", "Exponential", "GaussianNoise", "ImpulseNoise", "LaplacianNoise", "LeftShift", "Log", "Max", "Mean", "Median", "Min", "MultiplicativeNoise", "Multiply", "Or", "PoissonNoise", "Pow", "RightShift", "Set", "Sin", "Sine", "Subtract", "Sum", "Threshold", "ThresholdBlack", "ThresholdWhite", "UniformNoise", "Xor" ] eval { _flag = "-evaluate " ++ eval; Option_edit caption labels value = this.Evaluate labels?value; } evaluate_widget = Evaluate "Add"; Filter filt = class Option_string "Filter" [ "default", "Bartlett", "Blackman", "Bohman", "Box", "Catrom", "Cosine", "Cubic", "Gaussian", "Hamming", "Hann", "Hermite", "Jinc", "Kaiser", "Lagrange", "Lanczos", "Lanczos2", "Lanczos2Sharp", "LanczosRadius", "LanczosSharp", "Mitchell", "Parzen", "Point", "Quadratic", "Robidoux", "RobidouxSharp", "Sinc", "SincFast", "Spline", "Triangle", "Welch" ] filt { _flag = if filt == "default" then "" else "-filter " ++ filt; Option_edit caption labels value = this.Filter labels?value; } filter_widget = Filter "default"; Function func = class Option_string "Function" [ "Polynomial", "Sinusoid", "Arcsin", "Arctan" ] func { _flag = func; Option_edit caption labels value = this.Function labels?value; } function_widget = Function "Polynomial"; // "Polynomial (a[n], a[n-1], ... a[1], a[0])", // "Sinusoid (freq, phase, amp, bias)", // "Arcsin (width, centre, range, bias)", // "Arctan (slope, centre, range, bias)" Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; ImageType imagetype = class Option_string "Image type" [ "Bilevel", "ColorSeparation", "ColorSeparationAlpha", "ColorSeparationMatte", "Grayscale", "GrayscaleAlpha", "GrayscaleMatte", "Optimize", "Palette", "PaletteBilevelAlpha", "PaletteBilevelMatte", "PaletteAlpha", "PaletteMatte", "TrueColorAlpha", "TrueColorMatte", "TrueColor" ] imagetype { _flag = "-type " ++ imagetype; Option_edit caption labels value = this.ImageType labels?value; } imagetype_widget = ImageType "TrueColor"; Intensity intensity = class Option_string "Intensity (gray conversion)" [ "Average", "Brightness", "Lightness", "MS", "Rec601Luma", "Rec601Luminance", "Rec709Luma", "Rec709Luminance", "RMS" ] intensity { _flag = "-intensity " ++ intensity, has_intensity = ""; Option_edit caption labels value = this.Intensity labels?value; } intensity_widget = Intensity "Rec709Luminance"; Interpolate interp = class Option_string "Interpolate" [ "default", "Average", "Average4", "Average9", "Average16", "Background", "Bilinear", "Blend", "Integer", "Mesh", "Nearest", "NearestNeighbor", "Spline" ] interp { _flag = if interp == "default" then "" else "-interpolate " ++ interp; Option_edit caption labels value = this.Interpolate labels?value; } interpolate_widget = Interpolate "default"; Kernel kernel = class Option_string "Kernel" [ "Unity", "Gaussian", "DoG", "LoG", "Blur", "Comet", "Binomial", "Laplacian", "Sobel", "FreiChen", "Roberts", "Prewitt", "Compass", "Kirsch", "Diamond", "Square", "Rectangle", "Disk", "Octagon", "Plus", "Cross", "Ring", "Peaks", "Edges", "Corners", "Diagonals", "LineEnds", "LineJunctions", "Ridges", "ConvexHull", "ThinSe", "Skeleton", "Chebyshev", "Manhattan", "Octagonal", "Euclidean" // FIXME: custom kernel ] kernel { _flag = kernel; Option_edit caption labels value = this.Kernel labels?value; } kernel_widget = Kernel "Unity"; ModColSp msp = class Option_string "modulate colorspace" [ "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "LCH" ] msp { _flag = "-set option:modulate:colorspace " ++ msp; Option_edit caption labels value = this.ModColSp labels?value; } ModColSp_widget = ModColSp "HSL"; MorphMeth morph = class Option_string "Method" [ "Correlate", "Convolve", "Dilate", "Erode", "Close", "Open", "DilateIntensity", "ErodeIntensity", "CloseIntensity", "OpenIntensity", "Smooth", "EdgeOut", "EdgeIn", "Edge", "TopHat", "BottomHat", "HitAndMiss", "Thinning", "Thicken", "Distance", "IterativeDistance" ] morph { _flag = morph; Option_edit caption labels value = this.MorphMeth labels?value; } morphmeth_widget = MorphMeth "Dilate"; Noise noise = class Option_string "Noise" [ "Gaussian", "Impulse", "Laplacian", "Multiplicative", "Poisson", "Random", "Uniform" ] noise { _flag = "+noise " ++ noise; Option_edit caption labels value = this.Noise labels?value; } noise_widget = Noise "Gaussian"; Pattern pattern = class Option_string "Noise" [ // See http://www.imagemagick.org/script/formats.php "bricks", "checkerboard", "circles", "crosshatch", "crosshatch30", "crosshatch45", "gray0", "gray5", "gray10", "gray15", "gray20", "gray25", "gray30", "gray35", "gray40", "gray45", "gray50", "gray55", "gray60", "gray65", "gray70", "gray75", "gray80", "gray85", "gray90", "gray95", "gray100", "hexagons", "horizontal", "horizontal2", "horizontal3", "horizontalsaw", "hs_bdiagonal", "hs_cross", "hs_diagcross", "hs_fdiagonal", "hs_horizontal", "hs_vertical", "left30", "left45", "leftshingle", "octagons", "right30", "right45", "rightshingle", "smallfishscales", "vertical", "vertical2", "vertical3", "verticalbricks", "verticalleftshingle", "verticalrightshingle", "verticalsaw" ] pattern { _flag = "pattern:" ++ pattern; Option_edit caption labels value = this.Pattern labels?value; } pattern_widget = Pattern "bricks"; ResizeType resizet = class Option_string "Resize type" [ "resize", "scale", "sample", "adaptive-resize" ] resizet { _flag = resizet; Option_edit caption labels value = this.ResizeType labels?value; } ResizeType_widget = ResizeType "resize"; Size_widget = class { _vislevel = 3; width = Expression "Width (pixels)" 64; height = Expression "Height (pixels)" 64; _flag = "-size " ++ print width.expr ++ "x" ++ print height.expr; }; StatType statt = class Option_string "Statistic type" [ "Gradient", "Maximum", "Mean", "Median", "Minimum", "Mode", "Nonpeak", "StandardDeviation" ] statt { _flag = statt; Option_edit caption labels value = this.StatType labels?value; } StatType_widget = StatType "Mean"; VirtualPixel vp = class Option_string "Virtual pixel" [ "Background", "Black", "CheckerTile", "Dither", "Edge", "Gray", "HorizontalTile", "HorizontalTileEdge", "Mirror", "None", "Random", "Tile", "Transparent", "VerticalTile", "VerticalTileEdge", "White" ] vp { _flag = "-virtual-pixel " ++ vp; _isBackground = (vp == "Background"); Option_edit caption labels value = this.VirtualPixel labels?value; } VirtualPixel_widget = VirtualPixel "Edge"; VirtualPixelBack_widget = class { virtpix = Magick.VirtualPixel_widget; background = Magick.background_widget; _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") ++ virtpix._flag; } Geometry_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; AnnotGeometry_widget = class { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print shearX.expr, "x", print shearY.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; OffsetGeometry_widget = class { _vislevel = 3; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; WhxyGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; FrameGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; outbev = Expression "Outer bevel thickness" 0; inbev = Expression "Inner bevel thickness" 0; _flag = concat [print x.expr, "x", print y.expr, format outbev, format inbev] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } } ================================================ FILE: share/nip2/compat/8.4/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/8.4/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_tile_cache_random x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } decode im = oo_unary_function decode_op im, is_class im = decode_im im, is_image im = error (_ "bad arguments to " ++ "decode") { decode_op = Operator "decode" decode Operator_type.COMPOUND_REWRAP false; decode_im im = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK = im_rad2float im, get_coding im == Image_coding.RAD = im; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; image' = decode image; patches = map (\p extract_area p?0 p?1 sample_width sample_height image') cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h image = oo_unary_function hist_equalize_local_op image, is_class image = lhisteq image, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; // loop over bands, if necessary lhisteq im = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 = (foldl1 join @ map lhisteq @ bandsplit) im; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } reduce kernel xshr yshr image = oo_unary_function reduce_op image, is_class image = reduce_im image, is_image image = error (_ "bad arguments to " ++ "reduce") { reduce_op = Operator "reduce" reduce_im Operator_type.COMPOUND_REWRAP false; reduce_im im = out { [out] = vips_call "reduce" [im, xshr, yshr] [$kernel => kernel.value]; } } similarity interpolate scale angle image = oo_unary_function similarity_op image, is_class image = similarity_im image, is_image image = error (_ "bad arguments to " ++ "similarity") { similarity_op = Operator "similarity" similarity_im Operator_type.COMPOUND_REWRAP false; similarity_im im = out { [out] = vips_call "similarity" [im] [ $interpolate => interpolate.value, $scale => scale, $angle => angle ]; } } resize kernel xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; is_nn = kernel.type == Kernel_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn // everything else ... we just pass on to vips_resize() = vips_resize kernel xfac' yfac' im { vips_resize kernel hscale vscale im = out { [out] = vips_call "resize" [im, hscale] [$vscale => vscale, $kernel => kernel.value]; } } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C * * Also calculate R2, see eg.: * https://en.wikipedia.org/wiki/Coefficient_of_determination */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } ss = len xes; sx = sum xes; sy = sum yes; my = sy / ss; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; my = sy / len xes; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* One image in, one out. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } /* Two images in, one out. */ system_image2 command x1 x2 = oo_binary_function system_image2_op x1 x2, is_class x1 = oo_binary'_function system_image2_op x1 x2, is_class x2 = system x1 x2, is_image x1 && is_image x2 = error (_ "bad arguments to " ++ "system_image2") { system_image2_op = Operator "system_image2" (system_image2 command) Operator_type.COMPOUND_REWRAP false; system x1 x2 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Three images in, one out. */ system_image3 command x1 x2 x3 = oo_binary_function system_image2_op x2 x3, is_class x2 = oo_binary'_function system_image2_op x2 x3, is_class x3 = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 = error (_ "bad arguments to " ++ "system_image3") { system_image2_op = Operator "system_image2" (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; system x1 x2 x3 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2, x3], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Zero images in, one out. */ system_image0 command = Image image_out { [image_out] = vips_call "system" [command] [ $out => true, $out_format => "%s.tif" ]; } hough_line w h x = oo_unary_function hough_line_op x, is_class x = hline (to_real w) (to_real h) x, is_image x = error (_ "bad arguments to " ++ "hough_line") { hough_line_op = Operator "hough_line" (hough_line w h) Operator_type.COMPOUND_REWRAP false; hline w h x = pspace { [pspace] = vips_call "hough_line" [x] [ $width => w, $height => h ]; } } hough_circle s mn mx x = oo_unary_function hough_circle_op x, is_class x = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x = error (_ "bad arguments to " ++ "hough_circle") { hough_circle_op = Operator "hough_circle" (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; hcircle s mn mx x = pspace { [pspace] = vips_call "hough_circle" [x] [ $scale => s, $min_radius => mn, $max_radius => mx ]; } } mapim interp ind in = oo_binary_function mapim_op ind in, is_class ind = oo_binary'_function mapim_op ind in, is_class in = mapim_fn ind in, is_image ind && is_image in = error (_ "bad arguments to " ++ "mapim") { mapim_op = Operator "mapim" (mapim interp) Operator_type.COMPOUND_REWRAP false; mapim_fn ind im = out { [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; } } perlin cell width height = Image im { [im] = vips_call "perlin" [to_real width, to_real height] [ $cell_size => to_real cell ]; } worley cell width height = Image im { [im] = vips_call "worley" [to_real width, to_real height] [ $cell_size => to_real cell ]; } ================================================ FILE: share/nip2/compat/8.4/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Kernel_type = class { NEAREST_NEIGHBOUR = 0; LINEAR = 1; CUBIC = 2; LANCZOS2 = 3; LANCZOS3 = 4; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map kernel numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Linear", _ "Cubic", _ "Lanczos, two lobes", _ "Lanczos, three lobes" ]; /* And to vips enum nicknames. */ types = [ "nearest", "linear", "cubic", "lanczos2", "lanczos3" ]; } Kernel type = class { value = Kernel_type.types?type; } Kernel_linear = Kernel Kernel_type.LINEAR; Kernel_picker default = class Kernel kernel.value { _vislevel = 2; kernel = Option "Kernel" Kernel_type.descriptions default; } Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; to_image dpi = extract_bands 0 3 (graph_export_image (to_real dpi) this); } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/8.5/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = print_profile, has_type image && get_type image == Image_type.CMYK && has_bands image && get_bands image >= 4 = monitor_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/8.5/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 2; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 20; fs = Scale "Sharpen flat areas by" 0 5 0.5; js = Scale "Sharpen jaggy areas by" 0 5 1; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; max_slope = Scale "Maxium slope" 0 10 0; _result = map_unary process x { process in = hist_equalize_local window_width window_height max_slope in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_hough_item = class Menupullright "_Hough Transform" "transform to parameter space" { Line_item = class Menuaction "_Line" "find straight line Hough transform" { action a = class _result { _vislevel = 3; pspace_width = Expression "Parameter space width" 64; pspace_height = Expression "Parameter space height" 64; _result = map_unary line a { line a = hough_line (to_real pspace_width) (to_real pspace_height) a; } } } Circle_item = class Menuaction "_Circle" "find circle Hough transform" { action a = class _result { _vislevel = 3; scale = Expression "Scale down parameter space by" 10; min_radius = Expression "Minimum radius" 10; max_radius = Expression "Maximum radius" 30; _result = map_unary circle a { circle a = hough_circle (to_real scale) (to_real min_radius) (to_real max_radius) a; } } } } Filter_coordinate_item = class Menupullright "_Coordinate Transform" "various coordinate transforms" { // run a function which wants a complex arg on a non-complex two-band // image run_cmplx fn x = re x' ++ im x' { x' = fn (x?0, x?1); } Polar_item = class Menuaction "_Polar" "transform to polar coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_polar a { to_polar im = mapim interp.value map' im { // xy image, origin in the centre, scaled to fit image to // a circle xy = make_xy im.width im.height; xy' = xy - Vector [im.width / 2, im.height / 2]; scale = min [im.width, im.height] / im.width; xy'' = 2 * xy' / scale; // to polar, scale vertical axis to 360 degrees map = run_cmplx polar xy''; map' = map * Vector [1, im.height / 360]; } } } } Rectangular_item = class Menuaction "_Rectangular" "transform to rectangular coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_rect a { to_rect im = mapim interp.value map'' im { // xy image, vertical scaled to 360 degrees xy = make_xy im.width im.height; xy' = xy * Vector [1, 360 / im.height]; // to rect, scale to image rect map = run_cmplx rectangular xy'; scale = min [im.width, im.height] / im.width; map' = map * scale / 2; map'' = map' + Vector [im.width / 2, im.height / 2]; } } } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "Blend _Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } Autotrace_item = class Menuaction "_Trace" "convert a bitmap to an SVG file" { action x = class _result { _vislevel = 3; despeckle = Scale "Despeckle level" 1 20 1; line = Scale "Line threshold" 1 20 1; center = Toggle "Trace centreline" false; scale = Scale "SVG scale" 0.1 10 1; command = "autotrace %s " ++ join_sep " " [ofmt, ofile, desp, lint, cent] { prog = search_for_error "autotrace"; ofmt = "-output-format svg"; ofile = "-output-file %s"; desp = "-despeckle-level " ++ print despeckle.value; lint = "-line-threshold " ++ print line.value; cent = if center then "-centerline " else ""; } _result = Image output { [output] = vips_call "system" [command] [$in => [x.value], $in_format => "%s.ppm", $out => true, $out_format => "%s.svg[scale=" ++ print scale.value ++ "]" ]; } } } ================================================ FILE: share/nip2/compat/8.5/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = map_binary map x y { map a b = hist_find_indexed index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } Hist_entropy_item = class Menuaction "Entropy" "calculate histogram entropy" { action x = hist_entropy x; } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Kernel_linear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; caption = Expression "Chart caption" "none"; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; xcaption = Expression "X axis caption" "none"; ycaption = Expression "Y axis caption" "none"; series_captions = Expression "Series captions" ["Band 0"]; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range ++ captions; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; captions = concat (map test caption_options) ++ [$series_captions => series_captions.expr] { caption_options = [ $caption => caption.expr, $xcaption => xcaption.expr, $ycaption => ycaption.expr ]; test x = [], value == "none" = [option_name => value] { [option_name, value] = x; } } image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/8.5/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel h v image, aspect = resize kernel fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize kernel fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_map_item = class Menuaction "_Map" "map an image through a 2D transform image" { action a b = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_binary trans a b { trans a b = mapim interp.value in index { // get the index image first [index, in] = sortc (const is_twocomponent) [a, b]; // is a two-component image, ie. one band complex, or // two-band non-complex is_twocomponent x = is_nonc x || is_c x; is_nonc x = has_bands x && get_bands x == 2 && has_format x && !is_complex_format (get_format x); is_c x = has_bands x && get_bands x == 1 && has_format x && is_complex_format (get_format x); is_complex_format f = f == Image_format.COMPLEX || f == Image_format.DPCOMPLEX; } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1a = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_alpha_item = class Menupullright "_Alpha" "manipulate image alpha" { Add_item = class Menuaction "_Add" "add alpha" { action x = class _result { _vislevel = 3; opacity = Expression "Opacity (255 == solid)" 255; _result = x ++ to_real opacity; } } Flatten_item = class Menuaction "_Flatten" "flatten alpha out of image" { action x = class _result { _vislevel = 3; bg = Expression "Background" 0; _result = map_unary (flattenimage bg) x; } } Extract_item = class Menuaction "_Extract" "extract alpha" { action x = map_unary exb x { exb x = extract_bands (x.bands - 1) 1 x; } } Drop_item = class Menuaction "_Drop" "drop alpha" { action x = map_unary exb x { exb x = extract_bands 0 (x.bands - 1) x; } } sep1 = Menuseparator; Premultiply_item = class Menuaction "_Premultiply" "premultiply alpha" { action x = premultiply x; } Unpremultiply_item = class Menuaction "_Unpremultiply" "unpremultiply alpha" { action x = unpremultiply x; } sep2 = Menuseparator; Blend_alpha_item = Filter_blend_item.Blend_alpha_item; } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 0; sy = Expression "Start y" 0; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; snake = Toggle "Reverse the order of every other row" false; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _l' = map2 reverse_if_odd [0..] _l, snake = _l { reverse_if_odd n x = reverse x, n % 2 == 1 = x; } _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l')); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Noise_item = class Menupullright "_Noise" "various noise generators" { Gaussian_item = class Menuaction "_Gaussian" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (gaussnoise nwidth nheight mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal noise image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Perlin_item = class Menuaction "_Perlin" "Perlin noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; cell_size = Expression "Cell size (pixels)" 8; eight = Toggle "Eight bit output" true; _result = 128 * im + 128, eight = im { im = perlin cell_size nwidth nheight; } } } Worley_item = class Menuaction "_Worley" "Worley noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 512; nheight = Expression "Image height (pixels)" 512; cell_size = Expression "Cell size (pixels)" 256; _result = worley cell_size nwidth nheight; } } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/8.5/Magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate Magick.def 13-Apr-2014 snibgo Put "new image" items into sub-menu. New class VirtualPixlBack. 17-Apr-2014 snibgo Many small changes. A few new menu options. Created sub-menu for multi-input operations. 3-May-2014 jcupitt Put quotes around ( in shadow to help unix Last update: 17-Apr-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ // We don't need Noop. /*=== Magick_noop_item = class Menuaction "_Noop" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_testPar_item = class Menuaction "_TestPar" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "( +clone ) +append ", "\"%s\"" ]; _result = Magick.system command x; } } /* Removed Read_item and Write_item, much better to use nip2 load/save image. * Plus they can load all libMagick formats anyway. */ // Put "new image" items into sub-menu Magick_NewImageMenu_item = class Menupullright "_New image" "make a new image" { Magick_newcanvas_item = class Menuaction "_Solid colour" "make image of solid colour" { action = class _result { _vislevel = 3; size = Magick.Size_widget; colour = Magick.generalcol_widget; command = Magick.command [ size._flag, "\"canvas:" ++ colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_builtin_item = class Menuaction "_Built-in image" "create a built-in image" { action = class _result { _vislevel = 3; builtin = Magick.builtin_widget; command = Magick.command [ builtin._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_gradient_item = class Menuaction "_Gradient" "make a linear gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; topColour = Magick.generalcol_widget; bottomColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"gradient:", topColour._flag, "-", bottomColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } Magick_hald_item = class Menuaction "_Hald-clut image" "create an identity hald-clut image" { action = class _result { _vislevel = 3; order = Expression "order" 8; command = Magick.command [ "hald:" ++ print order.expr, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_pattern_item = class Menuaction "_Pattern" "create pattern" { action = class _result { _vislevel = 3; size = Magick.Size_widget; pattern = Magick.pattern_widget; command = Magick.command [ size._flag, pattern._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_plasma_item = class Menuaction "_Plasma image" "create plasma image" { action = class _result { _vislevel = 3; size = Magick.Size_widget; // FIXME? ColourA-ColourB. // FIXME? Allow plasma:fractal? command = Magick.command [ size._flag, "plasma:", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_radialgradient_item = class Menuaction "_Radial gradient" "make a radial gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; innerColour = Magick.generalcol_widget; outerColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"radial-gradient:", innerColour._flag, "-", outerColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } } // end Magick_NewImageMenu_item Magick_MultiMenu_item = class Menupullright "_Multiple inputs" "make an image from multiple images" { Magick_composite_item = class Menuaction "_Composite" "composite two images (without mask)" { action x y = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_compositeMask_item = class Menuaction "_Composite masked" "composite two images (with mask)" { action x y z = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system3 command x y z; } } // FIXME: other operations like remap that take another image as arguments are: // mask (pointless?), texture, tile (pointless?) // FIXME: operations that take a filename that isn't an image: // cdl, profile Magick_clut_item = class Menuaction "_Clut" "replace values using second image as colour look-up table" { action x y = class _result { _vislevel = 3; // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" command = Magick.command [ "\"%s\"", "\"%s\"", "-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_haldclut_item = class Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"", "-hald-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } // Encipher and decipher: key files can be text or image files. Magick_encipher_item = class Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { action x = class _result { _vislevel = 3; pathname = Pathname "Read key file" ""; isDecipher = Toggle "Decipher" false; command = Magick.command [ "\"%s\"", if pathname.value == "" then "" else ( ( if isDecipher then "-decipher " else "-encipher ") ++ ( "\"" ++ pathname.value ++ "\"" ) ), "\"%s\"" ]; _result = Magick.system command x; } } Magick_profile_item = class Menuaction "_Profile" "assigns/applies an ICC profile" { action x = class _result { _vislevel = 3; pathname1 = Pathname "Read profile file" ""; pathname2 = Pathname "Read profile file" ""; command = Magick.command [ "\"%s\"", if pathname1.value == "" then "" else ( "-profile " ++ "\"" ++ pathname1.value ++ "\""), if pathname2.value == "" then "" else ( "-profile " ++ "\"" ++ pathname2.value ++ "\""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_remap_item = class Menuaction "_Remap" "reduce colours to those in another image" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-remap", "\"%s\"", "\"%s\"" ]; _result = Magick.system2 command x y; } } } // end Magick_MultiMenu_item Magick_image_type_item = class Menuaction "_Image Type" "change image type" { action x = class _result { _vislevel = 3; imagetype = Magick.imagetype_widget; command = Magick.command [ "\"%s\"", imagetype._flag, "\"%s\"" ]; _result = Magick.system command x; } } sep2 = Menuseparator; Magick_alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = Magick.alpha_widget; command = Magick.command [ "\"%s\"", alpha._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = Magick.text_widget; font = Magick.Font_widget; geometry = Magick.AnnotGeometry_widget; gravity = Magick.gravity_widget; foreground = Magick.foreground_widget; undercol = Magick.undercol_widget; antialias = Magick.antialias_widget; command = Magick.command [ "\"%s\"", font._flag, antialias._flag, gravity._flag, foreground._flag, undercol._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoGamma_item = class Menuaction "_AutoGamma" "automatic gamma" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-gamma", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoLevel_item = class Menuaction "_AutoLevel" "automatic level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-level", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blurSharpMenu_item = class Menupullright "_Blur/Sharpen" "blur and sharpen" { Magick_adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; // note: adaptive-blur doesn't regard VP. command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-adaptive-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_blur_item = class Menuaction "_Blur" "blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gaussianBlur_item = class Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-gaussian-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_motionBlur_item = class Menuaction "_Motion Blur" "simulate motion blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; channels = Magick.ch_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-motion-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotationalBlur_item = class Menuaction "_RotationalBlur" "blur around the centre" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; angle = Scale "angle (degrees)" (-360) 360 20; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-radial-blur", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_selectiveBlur_item = class Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; threshold = Scale "Threshold (percent)" 0 100 50; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", intensity._flag, virtpixback._flag, channels._flag, "-selective-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print threshold.value ++ "%%", "\"%s\"" ]; _result = Magick.system command x; } } sep1 = Menuseparator; Magick_adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-adaptive-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sharpen_item = class Menuaction "_Sharpen" "sharpen" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_unsharpen_item = class Menuaction "_Unsharp" "sharpen with unsharp mask" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; gain = Scale "Gain" (-10) 10 1; threshold = Scale "Threshold" 0 1 0.05; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-unsharp", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print gain.value ++ "+" ++ print threshold.value, "\"%s\"" ]; _result = Magick.system command x; } } } // end BlurSharpMenu_item Magick_border_item = class Menuaction "_Border" "add border of given colour" { action x = class _result { _vislevel = 3; compose = Magick.compose_widget; width = Expression "Width" 3; bordercol = Magick.bordercol_widget; command = Magick.command [ "\"%s\"", compose._flag, bordercol._flag, "-border", print width.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_brightCont_item = class Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { action x = class _result { _vislevel = 3; bri = Scale "brightness" (-100) 100 0; con = Scale "contrast" (-100) 100 0; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-brightness-contrast", print bri.value ++ "x" ++ print con.value, "\"%s\"" ]; _result = Magick.system command x; } } // Note: canny requires ImageMagick 6.8.9-0 or later. Magick_canny_item = class Menuaction "_Canny" "detect a wide range of edges" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; lowPc = Scale "lower percent" 0 100 10; highPc = Scale "lower percent" 0 100 10; command = Magick.command [ "\"%s\"", "-canny", concat ["\"", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print lowPc.value ++ "%%+" ++ print highPc.value ++ "%%" ++ "\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_charcoal_item = class Menuaction "_Charcoal" "simulate a charcoal drawing" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; factor = Scale "factor" 0 50 1; command = Magick.command [ "\"%s\"", intensity._flag, "-charcoal", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_chop_item = class Menuaction "_Chop" "remove pixels from the interior" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-chop", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorize_item = class Menuaction "_Colorize" "colorize by given amount" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; val = Scale "value" 0 100 100; command = Magick.command [ "\"%s\"", foreground._flag, "-colorize", print val.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colors_item = class Menuaction "_Colors" "reduce number of colors" { action x = class _result { _vislevel = 3; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; quantize = Magick.colorspace_widget; colors = Expression "Colours" 3; command = Magick.command [ "\"%s\"", "-quantize", quantize._flag, "-treedepth", print treedepth.expr, dither._flag, "-colors", print colors.expr, "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: color-matrix? Magick_colorspace_item = class Menuaction "_Colourspace" "convert to arbitrary colourspace" { action x = class _result { _vislevel = 3; colsp = Magick.colorspace_widget; command = Magick.command [ "\"%s\"", "-colorspace", colsp._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorspaceGray_item = class Menuaction "_Colourspace gray" "convert to gray using given intensity method" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-colorspace gray", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrast_item = class Menuaction "_Contrast" "increase or reduce the contrast" { action x = class _result { _vislevel = 3; isReduce = Toggle "reduce contrast" false; command = Magick.command [ "\"%s\"", (if isReduce then "+" else "-") ++ "contrast", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrastStretch_item = class Menuaction "_Contrast stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-contrast-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: convolve (bias, kernel) Magick_crop_item = class Menuaction "_Crop" "cut out a rectangular region" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-crop", geometry._flag, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_deskew_item = class Menuaction "_Deskew" "straighten the image" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; // FIXME: toggle auto-crop? command = Magick.command [ "\"%s\"", "-deskew", "\"" ++ print threshold.value ++ "%%\"", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_despeckle_item = class Menuaction "_Despeckle" "reduce the speckles" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-despeckle", "\"%s\"" ]; _result = Magick.system command x; } } Magick_distort_item = class Menuaction "_Distort" "distort using a method and arguments" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; distort = Magick.distort_widget; args = String "Arguments" "1,0"; isPlus = Toggle "Extend to show entire image" false; command = Magick.command [ "\"%s\"", virtpixback._flag, (if isPlus then "+" else "-") ++ "distort", distort._flag, args.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_draw_item = class Menuaction "_Draw" "annotate with one or more graphic primitives" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; command = Magick.command [ "\"%s\"", foreground._flag, "-draw", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_edge_item = class Menuaction "_Edge" "detect edges" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-edge", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_emboss_item = class Menuaction "_Emboss" "emboss" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-emboss", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_enhance_item = class Menuaction "_Enhance" "enhance a noisy image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-enhance", "\"%s\"" ]; _result = Magick.system command x; } } Magick_equalize_item = class Menuaction "_Equalize" "equalize the histogram" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-equalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_evaluate_item = class Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; operation = Magick.evaluate_widget; val = Expression "value" 5; isPc = Toggle "Value is percent" false; command = Magick.command [ "\"%s\"", channels._flag, operation._flag, print val.expr ++ if isPc then "%%" else "", "\"%s\"" ]; _result = Magick.system command x; } } Magick_extent_item = class Menuaction "_Extent" "set the image size and offset" { action x = class _result { _vislevel = 3; background = Magick.background_widget; compose = Magick.compose_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, background._flag, gravity._flag, "-extent", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_FlipFlopMenu_item = class Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { Magick_flip_item = class Menuaction "_Flip vertically" "mirror upside-down" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flip", "\"%s\"" ]; _result = Magick.system command x; } } Magick_flop_item = class Menuaction "_Flop horizontally" "mirror left-right" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flop", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transpose_item = class Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transpose +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transverse_item = class Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transverse +repage", "\"%s\"" ]; _result = Magick.system command x; } } } // end Magick_FlipFlopMenu_item Magick_floodfill_item = class Menuaction "_Floodfill" "recolour neighbours that match" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; fuzz = Magick.fuzz_widget; coordinate = Magick.coordinate_widget; // -draw "color x,y floodfill" command = Magick.command [ "\"%s\"", foreground._flag, "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-draw \" color", coordinate._flag, "floodfill \"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_frame_item = class Menuaction "_Frame" "surround with border or beveled frame" { action x = class _result { _vislevel = 3; border = Magick.bordercol_widget; compose = Magick.compose_widget; matte = Magick.mattecol_widget; geometry = Magick.FrameGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, border._flag, matte._flag, "-frame", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_function_item = class Menuaction "_Function" "evaluate a function on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; function = Magick.function_widget; // FIXME: explain values; use sensible defaults. values = String "values" "0,0,0,0"; command = Magick.command [ "\"%s\"", channels._flag, "-function", function._flag, values.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_fx_item = class Menuaction "_Fx" "apply a mathematical expression" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; interpolate = Magick.interpolate_widget; virtpixback = Magick.VirtualPixelBack_widget; args = String "Expression" "u*1/2"; command = Magick.command [ "\"%s\"", channels._flag, interpolate._flag, virtpixback._flag, "-fx", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_gamma_item = class Menuaction "_Gamma" "apply a gamma correction" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; gamma = Magick.gamma_widget; command = Magick.command [ "\"%s\"", channels._flag, "-gamma", print gamma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradient_item = class Menuaction "_Gradient" "apply a linear gradient" { action x = class _result { _vislevel = 3; colourA = Magick.generalcol_widget; colourB = Magick.generalcol_widget; position = Option "colourA is at" [ "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"] 0; _baryArg = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 = "dunno"; command = Magick.command [ "\"%s\"", "-sparse-color barycentric \"" ++ _baryArg ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradientCorn_item = class Menuaction "_Gradient corners" "apply a bilinear gradient between the corners" { action x = class _result { _vislevel = 3; colour_top_left = Magick.generalcol_widget; colour_top_right = Magick.generalcol_widget; colour_bottom_left = Magick.generalcol_widget; colour_bottom_right = Magick.generalcol_widget; command = Magick.command [ "\"%s\"", "-sparse-color bilinear \"" ++ "0,0," ++ colour_top_left._flag ++ ",%%[fx:w-1],0" ++ colour_top_right._flag ++ ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", "+depth", "\"%s\"" ]; _result = Magick.system command x; } } Magick_histogram_item = class Menuaction "_Histogram" "make a histogram image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-define histogram:unique-colors=false histogram:" ++ "\"%s\"" ]; _result = Magick.system command x; } } Magick_implode_item = class Menuaction "_Implode" "implode pixels about the center" { action x = class _result { _vislevel = 3; factor = Scale "factor" 0 20 1; interpolate = Magick.interpolate_widget; // FIXME: virtual-pixel? command = Magick.command [ "\"%s\"", interpolate._flag, "-implode", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_level_item = class Menuaction "_Level" "adjust the level of channels" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "black point" (-100) 200 0; wht = Scale "white point" (-100) 200 100; gam = Scale "gamma" 0 30 1; isPc = Toggle "Levels are percent" true; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level", "\"" ++ print blk.value ++ "," ++ print wht.value ++ (if isPc then "%%" else "") ++ "," ++ print gam.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_levelCols_item = class Menuaction "_Level colors" "adjust levels to given colours" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; colour_black = Magick.generalcol_widget; colour_white = Magick.generalcol_widget; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level-colors", "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_linearStretch_item = class Menuaction "_Linear stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", channels._flag, "-linear-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_magnify_item = class Menuaction "_Magnify" "double the size of the image with pixel art scaling" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-magnify", "\"%s\"" ]; _result = Magick.system command x; } } Magick_modulate_item = class Menuaction "_Modulate" "modulate brightness, saturation and hue" { action x = class _result { _vislevel = 3; modcolsp = Magick.ModColSp_widget; bright = Scale "brightness" 0 200 100; sat = Scale "saturation" 0 200 100; hue = Scale "hue" 0 200 100; command = Magick.command [ "\"%s\"", modcolsp._flag, "-modulate", print bright.value ++ "," ++ print sat.value ++ "," ++ print hue.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_monochrome_item = class Menuaction "_Monochrome" "transform to black and white" { action x = class _result { _vislevel = 3; // FIXME: also intensity? intensity = Magick.intensity_widget; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; command = Magick.command [ "\"%s\"", intensity._flag, dither._flag, "-treedepth", print treedepth.expr, "-monochrome", "\"%s\"" ]; _result = Magick.system command x; } } Magick_morphology_item = class // See http://www.imagemagick.org/Usage/morphology/ Menuaction "_Morphology" "apply a morphological method" { action x = class _result { _vislevel = 3; method = Magick.morphmeth_widget; iter = Expression "Iterations (-1=repeat until done)" 1; kernel = Magick.kernel_widget; // FIXME: custom kernel eg "3x1+2+0:1,0,0" // width x height + offsx + offsy : {w*h values} // each value is 0.0 to 1.0 or "NaN" or "-" // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 // but // ring takes: radius1, radius2, scale // rectangle and comet take: width x height + offsx + offsy // blur takes: radius x sigma // FIXME: for now, simply allow any string input. kernel_arg = String "Kernel arguments" ""; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-morphology", method._flag ++ ":" ++ print iter.expr, kernel._flag ++ (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_negate_item = class Menuaction "_Negate" "negate" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-negate", "\"%s\"" ]; _result = Magick.system command x; } } Magick_addNoise_item = class Menuaction "_add Noise" "add noise" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; attenuate = Scale "attenuate" 0 1.0 1.0; noise = Magick.noise_widget; command = Magick.command [ "\"%s\"", channels._flag, "-attenuate", print attenuate.value, noise._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_normalize_item = class Menuaction "_Normalize" "normalize" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-normalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_opaque_item = class Menuaction "_Opaque" "change this colour to the fill colour" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; fill = Magick.foreground_widget; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", fill._flag, channels._flag, "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "opaque", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_paint_item = class Menuaction "_Paint" "simulate an oil painting" { action x = class _result { _vislevel = 3; rad = Expression "radius" 10; command = Magick.command [ "\"%s\"", "-paint", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } /*=== FIXME Bug; remove for now. Polaroid_item = class Menuaction "_Polaroid" "simulate a polaroid picture" { action x = class _result { _vislevel = 3; angle = Scale "angle" (-90) 90 20; command = Magick.command [ "\"%s\"", "-polaroid", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_posterize_item = class Menuaction "_Posterize" "reduce to (n) levels per channel" { action x = class _result { _vislevel = 3; levels = Expression "levels" 3; command = Magick.command [ "\"%s\"", "-posterize", print levels.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_raise_item = class Menuaction "_Raise" "lighten or darken image edges" { action x = class _result { _vislevel = 3; thk = Expression "Thickness" 3; command = Magick.command [ "\"%s\"", "-raise", print thk.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_resize_item = class Menuaction "_Resize" "resize to given width and height" { action x = class _result { _vislevel = 3; filter = Magick.filter_widget; type = Magick.ResizeType_widget; width = Scale "Width" 1 100 10; height = Scale "Height" 1 100 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", filter._flag, "-" ++ type._flag, "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "!") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_roll_item = class Menuaction "_Roll" "roll an image horizontally or vertically" { action x = class _result { _vislevel = 3; rollx = Expression "X" 3; rolly = Expression "Y" 3; command = Magick.command [ "\"%s\"", "-roll", (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotate_item = class Menuaction "_Rotate" "rotate" { action x = class _result { _vislevel = 3; angle = Scale "angle (degrees)" (-360) 360 20; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, "+distort", "SRT", print angle.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -segment, but cluster-threshold should be percentage of image area. Magick_sepia_item = class Menuaction "_Sepia tone" "simulate a sepia-toned photo" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; command = Magick.command [ "\"%s\"", "-sepia-tone", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shade_item = class Menuaction "_Shade" "shade with a distant light source" { action x = class _result { _vislevel = 3; azimuth = Scale "Azimuth (degrees)" (-360) 360 0; elevation = Scale "Elevation (degrees)" 0 90 45; command = Magick.command [ "\"%s\"", "-shade", print azimuth.value ++ "x" ++ print elevation.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_shadow_item = class Menuaction "_Shadow" "simulate a shadow" { action x = class _result { _vislevel = 3; shadowCol = Magick.generalcol_widget; opacity = Scale "Opacity (percent)" 0 100 75; sigma = Scale "Sigma" 0 30 2; // FIXME: make offsets a single widget? offsx = Scale "X-offset" (-20) 20 4; offsy = Scale "Y-offset" (-20) 20 4; arePc = Toggle "offsets are percentages" false; // FIXME: raw operation creates page offset, which vips dislikes. // So we take this futher, compositing with source. command = Magick.command [ "\"%s\"", "\"(\" +clone", "-background", "\"" ++ shadowCol._flag ++ "\"", "-shadow", concat [ "\"", print opacity.value, "x", print sigma.value, (if offsx.value >= 0 then "+" else ""), print offsx.value, (if offsy.value >= 0 then "+" else ""), print offsy.value, (if arePc then "%%" else ""), "\"" ], "\")\" +swap -background None -layers merge", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shave_item = class Menuaction "_Shave" "shave pixels from the edges" { action x = class _result { _vislevel = 3; width = Scale "Width" 0 50 10; height = Scale "Height" 0 50 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", "-shave", "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shear_item = class Menuaction "_Shear" "shear along the x-axis and/or y-axis" { action x = class _result { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-shear", print shearX.expr ++ "x" ++ print shearY.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sigmoid_item = class Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; contrast = Scale "contrast" 0 30 3; midpoint = Scale "mid-point (percent)" 0 100 50; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "sigmoidal-contrast", "\"" ++ print contrast.value ++ "x" ++ print midpoint.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_sketch_item = class Menuaction "_Sketch" "simulate a pencil sketch" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; command = Magick.command [ "\"%s\"", "-sketch", print radius.value ++ "x" ++ print sigma.value ++ (if angle >= 0 then ("+" ++ print angle.value) else ""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_solarize_item = class Menuaction "_Solarize" "negate all pixels above a threshold level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-solarize", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -sparse-color needs abitrary list of {x,y,colour}. Magick_splice_item = class Menuaction "_Splice" "splice a colour into the image" { action x = class _result { _vislevel = 3; background = Magick.background_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", background._flag, gravity._flag, "-splice", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_spread_item = class Menuaction "_Spread" "displace pixels by random amount" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; amount = Expression "Amount (pixels)" 5; command = Magick.command [ "\"%s\"", virtpixback._flag, "-spread", print amount.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_statistic_item = class Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { action x = class _result { _vislevel = 3; width = Expression "Width" 5; height = Expression "Height" 5; statisticType = Magick.StatType_widget; command = Magick.command [ "\"%s\"", "-statistic", statisticType._flag, print width.expr ++ "x" ++ print height.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = Magick.angle_widget; command = Magick.command [ "\"%s\"", "-swirl", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_thresholdMenu_item = class Menupullright "_Threshold" "make black or white" { Magick_threshold_item = class Menuaction "_Threshold" "apply black/white threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blackThreshold_item = class Menuaction "_Black threshold" "where below threshold set to black" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-black-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_whiteThreshold_item = class Menuaction "_White threshold" "where above threshold set to white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-white-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_latThreshold_item = class Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { action x = class _result { _vislevel = 3; width = Expression "Width" 10; height = Expression "Height" 10; offset = Scale "Offset (percent)" (-100) 100 0; // note: "-lat" doesn't respond to channels command = Magick.command [ "\"%s\"", "-lat", concat ["\"", print width.expr, "x", print height.expr, (if offset.value >= 0 then "+" else ""), print offset.value, "%%\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_randThreshold_item = class Menuaction "_Random Threshold" "between specified limits, apply random threshold" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; low = Scale "Low threshold" 0 100 10; high = Scale "High threshold" 0 100 90; isPc = Toggle "Thresholds are percent" true; command = Magick.command [ "\"%s\"", channels._flag, "-random-threshold", "\"" ++ print low.value ++ "x" ++ print high.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } } // end ThresholdMenu_item // Note: alternatives include: // convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif Magick_tile_item = class Menuaction "_Tile" "fill given size with tiled image" { action x = class _result { _vislevel = 3; size = Magick.Size_widget; command = Magick.command [ size._flag, "tile:" ++ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_tint_item = class Menuaction "_Tint" "apply a tint" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; amount = Scale "amount (percent)" 0 100 50; command = Magick.command [ "\"%s\"", foreground._flag, "-tint", // snibgo note: although the amount is a percentage, it doesn't need "%" character. print amount.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_transparent_item = class Menuaction "_Transparent" "make this colour transparent" { action x = class _result { _vislevel = 3; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "transparent", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_trim_item = class Menuaction "_Trim" "trims away border" { action x = class _result { _vislevel = 3; fuzz = Magick.fuzz_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-trim +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_uniqueCols_item = class Menuaction "_Unique colours" "discard all but one of any pixel color" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-unique-colors", "\"%s\"" ]; _result = Magick.system command x; } } Magick_vignette_item = class Menuaction "_Vignette" "soften the edges in vignette style" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; rx = Scale "Rolloff x (percent)" 0 100 10; ry = Scale "Rolloff y (percent)" 0 100 10; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-vignette", print radius.value ++ "x" ++ print sigma.value ++ (if rx.value >= 0 then "+" else "") ++ print rx.value ++ (if ry.value >= 0 then "+" else "") ++ print ry.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_wave_item = class Menuaction "_Wave" "shear the columns into a sine wave" { action x = class _result { _vislevel = 3; amplitude = Scale "Amplitude (pixels)" 0 100 10; wavelength = Scale "Wavelength (pixels)" 0 100 10; command = Magick.command [ "\"%s\"", "-wave", print amplitude.value ++ "x" ++ print wavelength.value, "\"%s\"" ]; _result = Magick.system command x; } } ================================================ FILE: share/nip2/compat/8.5/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/8.5 start_DATA = \ Math.def \ Image.def \ Magick.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ Preferences.ws \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _magick.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/8.5/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/8.5/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/8.5/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/8.5/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/8.5/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Kernel_linear xfactor yfactor scale_im; _offset_im = resize Kernel_linear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/8.5/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/8.5/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/8.5/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make a Vector ... works for Vector/Image/Real, plus image/real */ to_vector x = to_vector x.expr, is_Expression x = x, is_Vector x = oo_unary_function to_vector_op x, is_class x = tov x { to_vector_op = Operator "to_vector" tov Operator_type.COMPOUND false; tov x = Vector (itov x), is_image x = Vector [x], is_real x = Vector x, is_real_list x = Vector x?0, is_matrix x && len x == 1 = Vector (transpose x)?0, is_matrix x && len x?0 == 1 = error (_ "bad arguments to " ++ "to_vector"); itov i = v, is_image i = error (_ "not image") { m = im_vips2mask ((double) i); v = m.value?0, m.height == 1 = (transpose m.value)?0, m.width == 1 = error (_ "image is not 1xN or Nx1"); } } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart >= 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [RGB16, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; /* Return $PATH, reformatted as [["comp1", "comp2"], ["comp1", "comp2"] ..] */ system_search_path = [vipsbin] ++ map path_parse (split (equal path_sep) (expand "$PATH")) { /* On some platforms we ship vips with a few extra progs. Search * $VIPSHOME/bin first. */ vipsbin = path_parse (expand "$VIPSHOME") ++ ["bin"]; path_sep = ':', expand "$SEP" == "/" = ';'; } /* Search $PATH for the first occurence of name, or "". */ search_for name = hits?0, hits != [] = "" { exe_name = name ++ expand "$EXEEXT"; form_path p = path_absolute (p ++ [exe_name]); paths = map form_path system_search_path; hits = dropwhile (equal []) (map search paths); } /* Search $PATH for the first occurence of name, error on failure. */ search_for_error name = path, path != "" = error (exe_name ++ " not found on your search path. " ++ "Check you have installed the program and it is on your PATH.") { exe_name = name ++ expand "$EXEEXT"; path = search_for name; } ================================================ FILE: share/nip2/compat/8.5/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/compat/8.5/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Kernel_linear f1 1 b1 {b1 = resize Kernel_linear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/8.5/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/8.5/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/8.5/_magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate _magick.def Add 0-ary and 2-ary system Put utility funcs into a Magick class 11-Apr-2014 snibgo Added VirtualPixelBack for cases where background is only relevant when VP=Background 17-Apr-2014 snibgo Many small changes. 2-May-2014 jcupitt Added Magick.version 30-June-2014 Put single-quotes around command exe to help win 1-July-2014 Automatically fall back to gm if we can't find convert 17-July-2014 better GM support Last update: 17-July-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ /* Put these in a class to avoid filling the main namespace with IM stuff. */ Magick = class { // first gm on path, or "" gm_path = search_for "gm"; // first convert on $PATH, or "" // we check for the convert we ship first convert_path = vips_convert, vips_convert != "" = search_for "convert" { // the convert we ship with the vips binary on some platforms, or "" vips_convert = search (path_absolute convert) { vipshome = path_parse (expand "$VIPSHOME"); convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; } } use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; // Are we in GM or IM mode? use_gm = true, use_gm_pref && gm_path != "" = false, !use_gm_pref && convert_path != "" = false, convert_path != "" = true, gm_path != "" = error "neither IM nor GM executable found"; command_path = gm_path, use_gm = convert_path; // try to get the version as eg. [6, 7, 7, 10] // GM versions are smaller, typically [1, 3, 18] version = map parse_int (split (member ".-") version_string) { [output] = vips_call "system" ["'" ++ command_path ++ "' -version"] [$log=>true]; version_string = (split (equal ' ') output)?1, use_gm = (split (equal ' ') output)?2; } // make a command-line ... args is a [str] we join with spaces command args = "'" ++ command_path ++ "' " ++ join_sep " " args' { args' = ["convert"] ++ args, use_gm = args; } // capabilities ... different versions support different features, we // turn features on and off based on these // would probably be better to test for caps somehow has_intensity = false, use_gm = version?0 > 6 || version?1 > 7; has_channel = false, use_gm = version?0 > 6 || version?1 > 7; system0 cmd = system_image0 cmd; system cmd x = map_unary (system_image cmd) x; system2 cmd x y = map_binary (system_image2 cmd) x y; system3 cmd x y z = map_trinary (system_image3 cmd) x y z; radius_widget = Scale "Radius" 0 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle (degrees)" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; gamma_widget = Scale "Gamma" 0 10 1; colors_widget = Scale "Colors" 1 10 3; resize_widget = Scale "Resize (percent)" 0 500 100; fuzz_widget = Scale "Fuzz (percent)" 0 100 0; blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; // a colour with no enclosing quotes ... use this if we know there are // some quotes at an outer level print_colour_nq triple = concat ["#", concat (map fmt triple)] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } // we need the quotes because # is the comment character in *nix print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; GeneralCol triple = class Colour "sRGB" triple { _flag = print_colour_nq triple; Colour_edit space triple = this.GeneralCol triple; } generalcol_widget = GeneralCol [0, 0, 0]; Background triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" false; _flag = "-background " ++ if isNone then "None" else print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Bordercol triple = class Colour "sRGB" triple { _flag = "-bordercolor " ++ print_colour triple; Colour_edit space triple = this.Bordercol triple; } bordercol_widget = Bordercol [0, 0, 0]; Mattecol triple = class Colour "sRGB" triple { _flag = "-mattecolor " ++ print_colour triple; Colour_edit space triple = this.Mattecol triple; } mattecol_widget = Mattecol [189, 189, 189]; // FIXME: Undercolour, like many others, can have alpha channel. // How does user input this? With a slider? Undercol triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" true; _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); Colour_edit space triple = this.Undercol triple; } undercol_widget = Undercol [0, 0, 0]; changeCol_widget = class { _vislevel = 3; colour = GeneralCol [0, 0, 0]; fuzz = fuzz_widget; nonMatch = Toggle "change non-matching colours" false; } Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Remove", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Builtin builtin = class Option_string "Builtin" [ // See http://www.imagemagick.org/script/formats.php "rose:", "logo:", "wizard:", "granite:", "netscape:" ] builtin { _flag = builtin; Option_edit caption labels value = this.Builtin labels?value; } builtin_widget = Builtin "rose:"; channels_widget = class { // FIXME? Can we grey-out alpha when we have no alpha channel, // show CMY(K) instead of RGB(K) etc? // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. ChanR valueR = class Toggle "Red" valueR { _flag = "R", valueR = ""; Toggle_edit caption valueR = this.ChanR valueR; } channelR = ChanR true; ChanG valueG = class Toggle "Green" valueG { _flag = "G", valueG = ""; Toggle_edit caption valueG = this.ChanG valueG; } channelG = ChanG true; ChanB valueB = class Toggle "Blue" valueB { _flag = "B", valueB = ""; Toggle_edit caption valueB = this.ChanB valueB; } channelB = ChanB true; ChanK valueK = class Toggle "Black" valueK { _flag = "K", valueK = ""; Toggle_edit caption valueK = this.ChanK valueK; } channelK = ChanK true; ChanA valueA = class Toggle "Alpha" valueA { _flag = "A", valueA = ""; Toggle_edit caption valueA = this.ChanA valueA; } channelA = ChanA false; ChanSy valueSy = class Toggle "Sync" valueSy { _flag = ",sync", valueSy = ""; Toggle_edit caption valueSy = this.ChanSy valueSy; } channelSy = ChanSy true; _rgbka = concat [channelR._flag, channelG._flag, channelB._flag, channelK._flag, channelA._flag ]; _flag = "", _rgbka == "" || !has_channel = concat [ "-channel ", _rgbka, channelSy._flag ]; } ch_widget = channels_widget; Colorspace colsp = class Option_string "Colorspace" [ "CIELab", "CMY", "CMYK", "Gray", "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "Lab", "LCH", "LCHab", "LCHuv", "LMS", "Log", "Luv", "OHTA", "Rec601Luma", "Rec601YCbCr", "Rec709Luma", "Rec709YCbCr", "RGB", "scRGB", "sRGB", "Transparent", "XYZ", "YCbCr", "YDbDr", "YCC", "YIQ", "YPbPr", "YUV" ] colsp { _flag = colsp; Option_edit caption labels value = this.Colorspace labels?value; } colorspace_widget = Colorspace "sRGB"; Compose comp = class Option_string "Compose method" [ "Atop", "Blend", "Blur", "Bumpmap", "ChangeMask", "Clear", "ColorBurn", "ColorDodge", "Colorize", "CopyBlack", "CopyBlue", "CopyCyan", "CopyGreen", "Copy", "CopyMagenta", "CopyOpacity", "CopyRed", "CopyYellow", "Darken", "DarkenIntensity", "DivideDst", "DivideSrc", "Dst", "Difference", "Displace", "Dissolve", "Distort", "DstAtop", "DstIn", "DstOut", "DstOver", "Exclusion", "HardLight", "Hue", "In", "Lighten", "LightenIntensity", "LinearBurn", "LinearDodge", "LinearLight", "Luminize", "Mathematics", "MinusDst", "MinusSrc", "Modulate", "ModulusAdd", "ModulusSubtract", "Multiply", "None", "Out", "Overlay", "Over", "PegtopLight", "PinLight", "Plus", "Replace", "Saturate", "Screen", "SoftLight", "Src", "SrcAtop", "SrcIn", "SrcOut", "SrcOver", "VividLight", "Xor" ] comp { _flag = "-compose " ++ comp; Option_edit caption labels value = this.Compose labels?value; } compose_widget = Compose "Over"; // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack coordinate_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; _flag = concat [print x.expr, ",", print y.expr]; }; Distort distort = class Option_string "Distort" [ "Affine", "AffineProjection", "ScaleRotateTranslate", "SRT", "Perspective", "PerspectiveProjection", "BilinearForward", "BilinearReverse", "Polynomial", "Arc", "Polar", "DePolar", "Barrel", "BarrelInverse", "Shepards", "Resize" ] distort { _flag = distort; Option_edit caption labels value = this.Distort labels?value; } distort_widget = Distort "SRT"; Dither dither = class Option_string "Dither" [ "None", "FloydSteinberg", "Riemersma" ] dither { _flag = "-dither " ++ dither; Option_edit caption labels value = this.Dither labels?value; } dither_widget = Dither "FloydSteinberg"; Evaluate eval = class Option_string "Evaluate operation" [ "Abs", "Add", "AddModulus", "And", "Cos", "Cosine", "Divide", "Exp", "Exponential", "GaussianNoise", "ImpulseNoise", "LaplacianNoise", "LeftShift", "Log", "Max", "Mean", "Median", "Min", "MultiplicativeNoise", "Multiply", "Or", "PoissonNoise", "Pow", "RightShift", "Set", "Sin", "Sine", "Subtract", "Sum", "Threshold", "ThresholdBlack", "ThresholdWhite", "UniformNoise", "Xor" ] eval { _flag = "-evaluate " ++ eval; Option_edit caption labels value = this.Evaluate labels?value; } evaluate_widget = Evaluate "Add"; Filter filt = class Option_string "Filter" [ "default", "Bartlett", "Blackman", "Bohman", "Box", "Catrom", "Cosine", "Cubic", "Gaussian", "Hamming", "Hann", "Hermite", "Jinc", "Kaiser", "Lagrange", "Lanczos", "Lanczos2", "Lanczos2Sharp", "LanczosRadius", "LanczosSharp", "Mitchell", "Parzen", "Point", "Quadratic", "Robidoux", "RobidouxSharp", "Sinc", "SincFast", "Spline", "Triangle", "Welch" ] filt { _flag = if filt == "default" then "" else "-filter " ++ filt; Option_edit caption labels value = this.Filter labels?value; } filter_widget = Filter "default"; Function func = class Option_string "Function" [ "Polynomial", "Sinusoid", "Arcsin", "Arctan" ] func { _flag = func; Option_edit caption labels value = this.Function labels?value; } function_widget = Function "Polynomial"; // "Polynomial (a[n], a[n-1], ... a[1], a[0])", // "Sinusoid (freq, phase, amp, bias)", // "Arcsin (width, centre, range, bias)", // "Arctan (slope, centre, range, bias)" Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; ImageType imagetype = class Option_string "Image type" [ "Bilevel", "ColorSeparation", "ColorSeparationAlpha", "ColorSeparationMatte", "Grayscale", "GrayscaleAlpha", "GrayscaleMatte", "Optimize", "Palette", "PaletteBilevelAlpha", "PaletteBilevelMatte", "PaletteAlpha", "PaletteMatte", "TrueColorAlpha", "TrueColorMatte", "TrueColor" ] imagetype { _flag = "-type " ++ imagetype; Option_edit caption labels value = this.ImageType labels?value; } imagetype_widget = ImageType "TrueColor"; Intensity intensity = class Option_string "Intensity (gray conversion)" [ "Average", "Brightness", "Lightness", "MS", "Rec601Luma", "Rec601Luminance", "Rec709Luma", "Rec709Luminance", "RMS" ] intensity { _flag = "-intensity " ++ intensity, has_intensity = ""; Option_edit caption labels value = this.Intensity labels?value; } intensity_widget = Intensity "Rec709Luminance"; Interpolate interp = class Option_string "Interpolate" [ "default", "Average", "Average4", "Average9", "Average16", "Background", "Bilinear", "Blend", "Integer", "Mesh", "Nearest", "NearestNeighbor", "Spline" ] interp { _flag = if interp == "default" then "" else "-interpolate " ++ interp; Option_edit caption labels value = this.Interpolate labels?value; } interpolate_widget = Interpolate "default"; Kernel kernel = class Option_string "Kernel" [ "Unity", "Gaussian", "DoG", "LoG", "Blur", "Comet", "Binomial", "Laplacian", "Sobel", "FreiChen", "Roberts", "Prewitt", "Compass", "Kirsch", "Diamond", "Square", "Rectangle", "Disk", "Octagon", "Plus", "Cross", "Ring", "Peaks", "Edges", "Corners", "Diagonals", "LineEnds", "LineJunctions", "Ridges", "ConvexHull", "ThinSe", "Skeleton", "Chebyshev", "Manhattan", "Octagonal", "Euclidean" // FIXME: custom kernel ] kernel { _flag = kernel; Option_edit caption labels value = this.Kernel labels?value; } kernel_widget = Kernel "Unity"; ModColSp msp = class Option_string "modulate colorspace" [ "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "LCH" ] msp { _flag = "-set option:modulate:colorspace " ++ msp; Option_edit caption labels value = this.ModColSp labels?value; } ModColSp_widget = ModColSp "HSL"; MorphMeth morph = class Option_string "Method" [ "Correlate", "Convolve", "Dilate", "Erode", "Close", "Open", "DilateIntensity", "ErodeIntensity", "CloseIntensity", "OpenIntensity", "Smooth", "EdgeOut", "EdgeIn", "Edge", "TopHat", "BottomHat", "HitAndMiss", "Thinning", "Thicken", "Distance", "IterativeDistance" ] morph { _flag = morph; Option_edit caption labels value = this.MorphMeth labels?value; } morphmeth_widget = MorphMeth "Dilate"; Noise noise = class Option_string "Noise" [ "Gaussian", "Impulse", "Laplacian", "Multiplicative", "Poisson", "Random", "Uniform" ] noise { _flag = "+noise " ++ noise; Option_edit caption labels value = this.Noise labels?value; } noise_widget = Noise "Gaussian"; Pattern pattern = class Option_string "Noise" [ // See http://www.imagemagick.org/script/formats.php "bricks", "checkerboard", "circles", "crosshatch", "crosshatch30", "crosshatch45", "gray0", "gray5", "gray10", "gray15", "gray20", "gray25", "gray30", "gray35", "gray40", "gray45", "gray50", "gray55", "gray60", "gray65", "gray70", "gray75", "gray80", "gray85", "gray90", "gray95", "gray100", "hexagons", "horizontal", "horizontal2", "horizontal3", "horizontalsaw", "hs_bdiagonal", "hs_cross", "hs_diagcross", "hs_fdiagonal", "hs_horizontal", "hs_vertical", "left30", "left45", "leftshingle", "octagons", "right30", "right45", "rightshingle", "smallfishscales", "vertical", "vertical2", "vertical3", "verticalbricks", "verticalleftshingle", "verticalrightshingle", "verticalsaw" ] pattern { _flag = "pattern:" ++ pattern; Option_edit caption labels value = this.Pattern labels?value; } pattern_widget = Pattern "bricks"; ResizeType resizet = class Option_string "Resize type" [ "resize", "scale", "sample", "adaptive-resize" ] resizet { _flag = resizet; Option_edit caption labels value = this.ResizeType labels?value; } ResizeType_widget = ResizeType "resize"; Size_widget = class { _vislevel = 3; width = Expression "Width (pixels)" 64; height = Expression "Height (pixels)" 64; _flag = "-size " ++ print width.expr ++ "x" ++ print height.expr; }; StatType statt = class Option_string "Statistic type" [ "Gradient", "Maximum", "Mean", "Median", "Minimum", "Mode", "Nonpeak", "StandardDeviation" ] statt { _flag = statt; Option_edit caption labels value = this.StatType labels?value; } StatType_widget = StatType "Mean"; VirtualPixel vp = class Option_string "Virtual pixel" [ "Background", "Black", "CheckerTile", "Dither", "Edge", "Gray", "HorizontalTile", "HorizontalTileEdge", "Mirror", "None", "Random", "Tile", "Transparent", "VerticalTile", "VerticalTileEdge", "White" ] vp { _flag = "-virtual-pixel " ++ vp; _isBackground = (vp == "Background"); Option_edit caption labels value = this.VirtualPixel labels?value; } VirtualPixel_widget = VirtualPixel "Edge"; VirtualPixelBack_widget = class { virtpix = Magick.VirtualPixel_widget; background = Magick.background_widget; _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") ++ virtpix._flag; } Geometry_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; AnnotGeometry_widget = class { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print shearX.expr, "x", print shearY.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; OffsetGeometry_widget = class { _vislevel = 3; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; WhxyGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; FrameGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; outbev = Expression "Outer bevel thickness" 0; inbev = Expression "Inner bevel thickness" 0; _flag = concat [print x.expr, "x", print y.expr, format outbev, format inbev] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } } ================================================ FILE: share/nip2/compat/8.5/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/8.5/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_tile_cache_random x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } decode im = oo_unary_function decode_op im, is_class im = decode_im im, is_image im = error (_ "bad arguments to " ++ "decode") { decode_op = Operator "decode" decode Operator_type.COMPOUND_REWRAP false; decode_im im = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK = im_rad2float im, get_coding im == Image_coding.RAD = im; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; image' = decode image; patches = map (\p extract_area p?0 p?1 sample_width sample_height image') cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = im_hist_indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) hist_find_indexed) Operator_type.COMPOUND false; } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h l image = oo_unary_function hist_equalize_local_op image, is_class image = out, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h l) Operator_type.COMPOUND_REWRAP false; [out] = vips_call "hist_local" [image, to_real w, to_real h] [$max_slope => to_real l]; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } reduce kernel xshr yshr image = oo_unary_function reduce_op image, is_class image = reduce_im image, is_image image = error (_ "bad arguments to " ++ "reduce") { reduce_op = Operator "reduce" reduce_im Operator_type.COMPOUND_REWRAP false; reduce_im im = out { [out] = vips_call "reduce" [im, xshr, yshr] [$kernel => kernel.value]; } } similarity interpolate scale angle image = oo_unary_function similarity_op image, is_class image = similarity_im image, is_image image = error (_ "bad arguments to " ++ "similarity") { similarity_op = Operator "similarity" similarity_im Operator_type.COMPOUND_REWRAP false; similarity_im im = out { [out] = vips_call "similarity" [im] [ $interpolate => interpolate.value, $scale => scale, $angle => angle ]; } } resize kernel xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; is_nn = kernel.type == Kernel_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn // everything else ... we just pass on to vips_resize() = vips_resize kernel xfac' yfac' im { vips_resize kernel hscale vscale im = out { [out] = vips_call "resize" [im, hscale] [$vscale => vscale, $kernel => kernel.value]; } } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C * * Also calculate R2, see eg.: * https://en.wikipedia.org/wiki/Coefficient_of_determination */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } ss = len xes; sx = sum xes; sy = sum yes; my = sy / ss; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; my = sy / len xes; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* One image in, one out. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } /* Two images in, one out. */ system_image2 command x1 x2 = oo_binary_function system_image2_op x1 x2, is_class x1 = oo_binary'_function system_image2_op x1 x2, is_class x2 = system x1 x2, is_image x1 && is_image x2 = error (_ "bad arguments to " ++ "system_image2") { system_image2_op = Operator "system_image2" (system_image2 command) Operator_type.COMPOUND_REWRAP false; system x1 x2 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Three images in, one out. */ system_image3 command x1 x2 x3 = oo_binary_function system_image2_op x2 x3, is_class x2 = oo_binary'_function system_image2_op x2 x3, is_class x3 = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 = error (_ "bad arguments to " ++ "system_image3") { system_image2_op = Operator "system_image2" (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; system x1 x2 x3 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2, x3], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Zero images in, one out. */ system_image0 command = Image image_out { [image_out] = vips_call "system" [command] [ $out => true, $out_format => "%s.tif" ]; } hough_line w h x = oo_unary_function hough_line_op x, is_class x = hline (to_real w) (to_real h) x, is_image x = error (_ "bad arguments to " ++ "hough_line") { hough_line_op = Operator "hough_line" (hough_line w h) Operator_type.COMPOUND_REWRAP false; hline w h x = pspace { [pspace] = vips_call "hough_line" [x] [ $width => w, $height => h ]; } } hough_circle s mn mx x = oo_unary_function hough_circle_op x, is_class x = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x = error (_ "bad arguments to " ++ "hough_circle") { hough_circle_op = Operator "hough_circle" (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; hcircle s mn mx x = pspace { [pspace] = vips_call "hough_circle" [x] [ $scale => s, $min_radius => mn, $max_radius => mx ]; } } mapim interp ind in = oo_binary_function mapim_op ind in, is_class ind = oo_binary'_function mapim_op ind in, is_class in = mapim_fn ind in, is_image ind && is_image in = error (_ "bad arguments to " ++ "mapim") { mapim_op = Operator "mapim" (mapim interp) Operator_type.COMPOUND_REWRAP false; mapim_fn ind im = out { [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; } } perlin cell width height = Image im { [im] = vips_call "perlin" [to_real width, to_real height] [ $cell_size => to_real cell ]; } worley cell width height = Image im { [im] = vips_call "worley" [to_real width, to_real height] [ $cell_size => to_real cell ]; } gaussnoise width height mean sigma = im { [im] = vips_call "gaussnoise" [to_real width, to_real height] [ $mean => to_real mean, $sigma => to_real sigma ]; } flattenimage bg x = oo_unary_function flatten_op x, is_class x = flt (to_vector bg) x, is_image x = error (_ "bad arguments to " ++ "flattenimage") { flatten_op = Operator "flatten" (flattenimage bg) Operator_type.COMPOUND_REWRAP false; flt bg x = out { [out] = vips_call "flatten" [x] [ $background => bg.value ]; } } premultiply x = oo_unary_function premultiply_op x, is_class x = prem x, is_image x = error (_ "bad arguments to " ++ "premultiply") { premultiply_op = Operator "premultiply" premultiply Operator_type.COMPOUND_REWRAP false; prem x = out { [out] = vips_call "premultiply" [x] [ ]; } } unpremultiply x = oo_unary_function unpremultiply_op x, is_class x = unprem x, is_image x = error (_ "bad arguments to " ++ "unpremultiply") { unpremultiply_op = Operator "unpremultiply" unpremultiply Operator_type.COMPOUND_REWRAP false; unprem x = out { [out] = vips_call "unpremultiply" [x] [ ]; } } hist_entropy x = oo_unary_function hist_entropy_op x, is_class x = entropy x, is_image x = error (_ "bad arguments to " ++ "hist_entropy") { hist_entropy_op = Operator "hist_entropy" hist_entropy Operator_type.COMPOUND_REWRAP false; entropy x = out { [out] = vips_call "hist_entropy" [x] [ ]; } } ================================================ FILE: share/nip2/compat/8.5/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options generated from this, so match the order to the order in the * Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Kernel_type = class { NEAREST_NEIGHBOUR = 0; LINEAR = 1; CUBIC = 2; LANCZOS2 = 3; LANCZOS3 = 4; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map kernel numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Linear", _ "Cubic", _ "Lanczos, two lobes", _ "Lanczos, three lobes" ]; /* And to vips enum nicknames. */ types = [ "nearest", "linear", "cubic", "lanczos2", "lanczos3" ]; } Kernel type = class { value = Kernel_type.types?type; } Kernel_linear = Kernel Kernel_type.LINEAR; Kernel_picker default = class Kernel kernel.value { _vislevel = 2; kernel = Option "Kernel" Kernel_type.descriptions default; } Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; to_image dpi = extract_bands 0 3 (graph_export_image (to_real dpi) this); } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } ================================================ FILE: share/nip2/compat/8.6/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } scRGB_item = class Menuaction (_ "_scRGB") (_ "convert to scRGB colourspace") { action x = conv Image_type.scRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } scRGB_item = class Menuaction (_ "_scRGB") (_ "tag as being in scRGB colourspace") { action x = tag Image_type.scRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = print_profile, has_type image && get_type image == Image_type.CMYK && has_bands image && get_bands image >= 4 = monitor_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/compat/8.6/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x { sobel im = abs (a - 128) + abs (b - 128) { a = conv filter_sobel im; b = conv (rot270 filter_sobel) im; } } } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 2; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 20; fs = Scale "Sharpen flat areas by" 0 5 0.5; js = Scale "Sharpen jaggy areas by" 0 5 1; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; max_slope = Scale "Maxium slope" 0 10 0; _result = map_unary process x { process in = hist_equalize_local window_width window_height max_slope in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_hough_item = class Menupullright "_Hough Transform" "transform to parameter space" { Line_item = class Menuaction "_Line" "find straight line Hough transform" { action a = class _result { _vislevel = 3; pspace_width = Expression "Parameter space width" 64; pspace_height = Expression "Parameter space height" 64; _result = map_unary line a { line a = hough_line (to_real pspace_width) (to_real pspace_height) a; } } } Circle_item = class Menuaction "_Circle" "find circle Hough transform" { action a = class _result { _vislevel = 3; scale = Expression "Scale down parameter space by" 10; min_radius = Expression "Minimum radius" 10; max_radius = Expression "Maximum radius" 30; _result = map_unary circle a { circle a = hough_circle (to_real scale) (to_real min_radius) (to_real max_radius) a; } } } } Filter_coordinate_item = class Menupullright "_Coordinate Transform" "various coordinate transforms" { // run a function which wants a complex arg on a non-complex two-band // image run_cmplx fn x = re x' ++ im x' { x' = fn (x?0, x?1); } Polar_item = class Menuaction "_Polar" "transform to polar coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_polar a { to_polar im = mapim interp.value map' im { // xy image, origin in the centre, scaled to fit image to // a circle xy = make_xy im.width im.height; xy' = xy - Vector [im.width / 2, im.height / 2]; scale = min [im.width, im.height] / im.width; xy'' = 2 * xy' / scale; // to polar, scale vertical axis to 360 degrees map = run_cmplx polar xy''; map' = map * Vector [1, im.height / 360]; } } } } Rectangular_item = class Menuaction "_Rectangular" "transform to rectangular coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_rect a { to_rect im = mapim interp.value map'' im { // xy image, vertical scaled to 360 degrees xy = make_xy im.width im.height; xy' = xy * Vector [1, 360 / im.height]; // to rect, scale to image rect map = run_cmplx rectangular xy'; scale = min [im.width, im.height] / im.width; map' = map * scale / 2; map'' = map' + Vector [im.width / 2, im.height / 2]; } } } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "Blend _Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } Autotrace_item = class Menuaction "_Trace" "convert a bitmap to an SVG file" { action x = class _result { _vislevel = 3; despeckle = Scale "Despeckle level" 1 20 1; line = Scale "Line threshold" 1 20 1; center = Toggle "Trace centreline" false; scale = Scale "SVG scale" 0.1 10 1; command = "autotrace %s " ++ join_sep " " [ofmt, ofile, desp, lint, cent] { prog = search_for_error "autotrace"; ofmt = "-output-format svg"; ofile = "-output-file %s"; desp = "-despeckle-level " ++ print despeckle.value; lint = "-line-threshold " ++ print line.value; cent = if center then "-centerline " else ""; } _result = Image output { [output] = vips_call "system" [command] [$in => [x.value], $in_format => "%s.ppm", $out => true, $out_format => "%s.svg[scale=" ++ print scale.value ++ "]" ]; } } } ================================================ FILE: share/nip2/compat/8.6/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = class _result { _vislevel = 3; combine = Combine_picker Combine_type.SUM; _result = map_binary map x y { map a b = hist_find_indexed combine.value index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } Hist_entropy_item = class Menuaction "Entropy" "calculate histogram entropy" { action x = hist_entropy x; } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Kernel_linear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; caption = Expression "Chart caption" "none"; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; xcaption = Expression "X axis caption" "none"; ycaption = Expression "Y axis caption" "none"; series_captions = Expression "Series captions" ["Band 0"]; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range ++ captions; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; captions = concat (map test caption_options) ++ [$series_captions => series_captions.expr] { caption_options = [ $caption => caption.expr, $xcaption => xcaption.expr, $ycaption => ycaption.expr ]; test x = [], value == "none" = [option_name => value] { [option_name, value] = x; } } image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/compat/8.6/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel h v image, aspect = resize kernel fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize kernel fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_map_item = class Menuaction "_Map" "map an image through a 2D transform image" { action a b = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_binary trans a b { trans a b = mapim interp.value in index { // get the index image first [index, in] = sortc (const is_twocomponent) [a, b]; // is a two-component image, ie. one band complex, or // two-band non-complex is_twocomponent x = is_nonc x || is_c x; is_nonc x = has_bands x && get_bands x == 2 && has_format x && !is_complex_format (get_format x); is_c x = has_bands x && get_bands x == 1 && has_format x && is_complex_format (get_format x); is_complex_format f = f == Image_format.COMPLEX || f == Image_format.DPCOMPLEX; } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1a = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_alpha_item = class Menupullright "_Alpha" "manipulate image alpha" { Add_item = class Menuaction "_Add" "add alpha" { action x = class _result { _vislevel = 3; opacity = Expression "Opacity (255 == solid)" 255; _result = x ++ to_real opacity; } } Flatten_item = class Menuaction "_Flatten" "flatten alpha out of image" { action x = class _result { _vislevel = 3; bg = Expression "Background" 0; _result = map_unary (flattenimage bg) x; } } Extract_item = class Menuaction "_Extract" "extract alpha" { action x = map_unary exb x { exb x = extract_bands (x.bands - 1) 1 x; } } Drop_item = class Menuaction "_Drop" "drop alpha" { action x = map_unary exb x { exb x = extract_bands 0 (x.bands - 1) x; } } sep1 = Menuseparator; Premultiply_item = class Menuaction "_Premultiply" "premultiply alpha" { action x = premultiply x; } Unpremultiply_item = class Menuaction "_Unpremultiply" "unpremultiply alpha" { action x = unpremultiply x; } sep2 = Menuseparator; Composite2_item = class Menuaction "_Composite two" "composite a pair of images" { action x y = class _result { _vislevel = 3; blend = Option_enum (_ "Blend mode") modes "over" { modes = Blend_type.types; } compositing_space = Option_enum (_ "Compositing space") spaces "sRGB" { spaces = Image_type.image_colour_spaces; } premultiplied = Toggle (_ "Premultiplied") false; _result = Image output { [output] = vips_call "composite" [[y.value, x.value], blend.value] [$compositing_space => compositing_space.value_thing, $premultiplied => premultiplied.value ]; } } } Composite3_item = class Menuaction "_Composite three" "composite three images" { action x y z = class _result { _vislevel = 3; blend1 = Option_enum (_ "Blend mode") modes "over" { modes = Blend_type.types; } blend2 = Option_enum (_ "Blend mode") modes "over" { modes = Blend_type.types; } compositing_space = Option_enum (_ "Compositing space") spaces "sRGB" { spaces = Image_type.image_colour_spaces; } premultiplied = Toggle (_ "Premultiplied") false; _result = Image output { [output] = vips_call "composite" [[z.value, y.value, x.value], [blend1.value, blend2.value]] [$compositing_space => compositing_space.value_thing, $premultiplied => premultiplied.value ]; } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 0; sy = Expression "Start y" 0; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; snake = Toggle "Reverse the order of every other row" false; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _l' = map2 reverse_if_odd [0..] _l, snake = _l { reverse_if_odd n x = reverse x, n % 2 == 1 = x; } _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l')); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Noise_item = class Menupullright "_Noise" "various noise generators" { Gaussian_item = class Menuaction "_Gaussian" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (gaussnoise nwidth nheight mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal noise image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Perlin_item = class Menuaction "_Perlin" "Perlin noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; cell_size = Expression "Cell size (pixels)" 8; eight = Toggle "Eight bit output" true; _result = 128 * im + 128, eight = im { im = perlin cell_size nwidth nheight; } } } Worley_item = class Menuaction "_Worley" "Worley noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 512; nheight = Expression "Image height (pixels)" 512; cell_size = Expression "Cell size (pixels)" 256; _result = worley cell_size nwidth nheight; } } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/compat/8.6/Magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate Magick.def 13-Apr-2014 snibgo Put "new image" items into sub-menu. New class VirtualPixlBack. 17-Apr-2014 snibgo Many small changes. A few new menu options. Created sub-menu for multi-input operations. 3-May-2014 jcupitt Put quotes around ( in shadow to help unix Last update: 17-Apr-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ // We don't need Noop. /*=== Magick_noop_item = class Menuaction "_Noop" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_testPar_item = class Menuaction "_TestPar" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "( +clone ) +append ", "\"%s\"" ]; _result = Magick.system command x; } } /* Removed Read_item and Write_item, much better to use nip2 load/save image. * Plus they can load all libMagick formats anyway. */ // Put "new image" items into sub-menu Magick_NewImageMenu_item = class Menupullright "_New image" "make a new image" { Magick_newcanvas_item = class Menuaction "_Solid colour" "make image of solid colour" { action = class _result { _vislevel = 3; size = Magick.Size_widget; colour = Magick.generalcol_widget; command = Magick.command [ size._flag, "\"canvas:" ++ colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_builtin_item = class Menuaction "_Built-in image" "create a built-in image" { action = class _result { _vislevel = 3; builtin = Magick.builtin_widget; command = Magick.command [ builtin._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_gradient_item = class Menuaction "_Gradient" "make a linear gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; topColour = Magick.generalcol_widget; bottomColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"gradient:", topColour._flag, "-", bottomColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } Magick_hald_item = class Menuaction "_Hald-clut image" "create an identity hald-clut image" { action = class _result { _vislevel = 3; order = Expression "order" 8; command = Magick.command [ "hald:" ++ print order.expr, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_pattern_item = class Menuaction "_Pattern" "create pattern" { action = class _result { _vislevel = 3; size = Magick.Size_widget; pattern = Magick.pattern_widget; command = Magick.command [ size._flag, pattern._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_plasma_item = class Menuaction "_Plasma image" "create plasma image" { action = class _result { _vislevel = 3; size = Magick.Size_widget; // FIXME? ColourA-ColourB. // FIXME? Allow plasma:fractal? command = Magick.command [ size._flag, "plasma:", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_radialgradient_item = class Menuaction "_Radial gradient" "make a radial gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; innerColour = Magick.generalcol_widget; outerColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"radial-gradient:", innerColour._flag, "-", outerColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } } // end Magick_NewImageMenu_item Magick_MultiMenu_item = class Menupullright "_Multiple inputs" "make an image from multiple images" { Magick_composite_item = class Menuaction "_Composite" "composite two images (without mask)" { action x y = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_compositeMask_item = class Menuaction "_Composite masked" "composite two images (with mask)" { action x y z = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system3 command x y z; } } // FIXME: other operations like remap that take another image as arguments are: // mask (pointless?), texture, tile (pointless?) // FIXME: operations that take a filename that isn't an image: // cdl, profile Magick_clut_item = class Menuaction "_Clut" "replace values using second image as colour look-up table" { action x y = class _result { _vislevel = 3; // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" command = Magick.command [ "\"%s\"", "\"%s\"", "-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_haldclut_item = class Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"", "-hald-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } // Encipher and decipher: key files can be text or image files. Magick_encipher_item = class Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { action x = class _result { _vislevel = 3; pathname = Pathname "Read key file" ""; isDecipher = Toggle "Decipher" false; command = Magick.command [ "\"%s\"", if pathname.value == "" then "" else ( ( if isDecipher then "-decipher " else "-encipher ") ++ ( "\"" ++ pathname.value ++ "\"" ) ), "\"%s\"" ]; _result = Magick.system command x; } } Magick_profile_item = class Menuaction "_Profile" "assigns/applies an ICC profile" { action x = class _result { _vislevel = 3; pathname1 = Pathname "Read profile file" ""; pathname2 = Pathname "Read profile file" ""; command = Magick.command [ "\"%s\"", if pathname1.value == "" then "" else ( "-profile " ++ "\"" ++ pathname1.value ++ "\""), if pathname2.value == "" then "" else ( "-profile " ++ "\"" ++ pathname2.value ++ "\""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_remap_item = class Menuaction "_Remap" "reduce colours to those in another image" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-remap", "\"%s\"", "\"%s\"" ]; _result = Magick.system2 command x y; } } } // end Magick_MultiMenu_item Magick_image_type_item = class Menuaction "_Image Type" "change image type" { action x = class _result { _vislevel = 3; imagetype = Magick.imagetype_widget; command = Magick.command [ "\"%s\"", imagetype._flag, "\"%s\"" ]; _result = Magick.system command x; } } sep2 = Menuseparator; Magick_alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = Magick.alpha_widget; command = Magick.command [ "\"%s\"", alpha._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = Magick.text_widget; font = Magick.Font_widget; geometry = Magick.AnnotGeometry_widget; gravity = Magick.gravity_widget; foreground = Magick.foreground_widget; undercol = Magick.undercol_widget; antialias = Magick.antialias_widget; command = Magick.command [ "\"%s\"", font._flag, antialias._flag, gravity._flag, foreground._flag, undercol._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoGamma_item = class Menuaction "_AutoGamma" "automatic gamma" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-gamma", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoLevel_item = class Menuaction "_AutoLevel" "automatic level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-level", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blurSharpMenu_item = class Menupullright "_Blur/Sharpen" "blur and sharpen" { Magick_adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; // note: adaptive-blur doesn't regard VP. command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-adaptive-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_blur_item = class Menuaction "_Blur" "blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gaussianBlur_item = class Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-gaussian-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_motionBlur_item = class Menuaction "_Motion Blur" "simulate motion blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; channels = Magick.ch_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-motion-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotationalBlur_item = class Menuaction "_RotationalBlur" "blur around the centre" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; angle = Scale "angle (degrees)" (-360) 360 20; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-radial-blur", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_selectiveBlur_item = class Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; threshold = Scale "Threshold (percent)" 0 100 50; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", intensity._flag, virtpixback._flag, channels._flag, "-selective-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print threshold.value ++ "%%", "\"%s\"" ]; _result = Magick.system command x; } } sep1 = Menuseparator; Magick_adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-adaptive-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sharpen_item = class Menuaction "_Sharpen" "sharpen" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_unsharpen_item = class Menuaction "_Unsharp" "sharpen with unsharp mask" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; gain = Scale "Gain" (-10) 10 1; threshold = Scale "Threshold" 0 1 0.05; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-unsharp", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print gain.value ++ "+" ++ print threshold.value, "\"%s\"" ]; _result = Magick.system command x; } } } // end BlurSharpMenu_item Magick_border_item = class Menuaction "_Border" "add border of given colour" { action x = class _result { _vislevel = 3; compose = Magick.compose_widget; width = Expression "Width" 3; bordercol = Magick.bordercol_widget; command = Magick.command [ "\"%s\"", compose._flag, bordercol._flag, "-border", print width.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_brightCont_item = class Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { action x = class _result { _vislevel = 3; bri = Scale "brightness" (-100) 100 0; con = Scale "contrast" (-100) 100 0; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-brightness-contrast", print bri.value ++ "x" ++ print con.value, "\"%s\"" ]; _result = Magick.system command x; } } // Note: canny requires ImageMagick 6.8.9-0 or later. Magick_canny_item = class Menuaction "_Canny" "detect a wide range of edges" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; lowPc = Scale "lower percent" 0 100 10; highPc = Scale "lower percent" 0 100 10; command = Magick.command [ "\"%s\"", "-canny", concat ["\"", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print lowPc.value ++ "%%+" ++ print highPc.value ++ "%%" ++ "\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_charcoal_item = class Menuaction "_Charcoal" "simulate a charcoal drawing" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; factor = Scale "factor" 0 50 1; command = Magick.command [ "\"%s\"", intensity._flag, "-charcoal", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_chop_item = class Menuaction "_Chop" "remove pixels from the interior" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-chop", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorize_item = class Menuaction "_Colorize" "colorize by given amount" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; val = Scale "value" 0 100 100; command = Magick.command [ "\"%s\"", foreground._flag, "-colorize", print val.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colors_item = class Menuaction "_Colors" "reduce number of colors" { action x = class _result { _vislevel = 3; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; quantize = Magick.colorspace_widget; colors = Expression "Colours" 3; command = Magick.command [ "\"%s\"", "-quantize", quantize._flag, "-treedepth", print treedepth.expr, dither._flag, "-colors", print colors.expr, "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: color-matrix? Magick_colorspace_item = class Menuaction "_Colourspace" "convert to arbitrary colourspace" { action x = class _result { _vislevel = 3; colsp = Magick.colorspace_widget; command = Magick.command [ "\"%s\"", "-colorspace", colsp._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorspaceGray_item = class Menuaction "_Colourspace gray" "convert to gray using given intensity method" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-colorspace gray", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrast_item = class Menuaction "_Contrast" "increase or reduce the contrast" { action x = class _result { _vislevel = 3; isReduce = Toggle "reduce contrast" false; command = Magick.command [ "\"%s\"", (if isReduce then "+" else "-") ++ "contrast", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrastStretch_item = class Menuaction "_Contrast stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-contrast-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: convolve (bias, kernel) Magick_crop_item = class Menuaction "_Crop" "cut out a rectangular region" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-crop", geometry._flag, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_deskew_item = class Menuaction "_Deskew" "straighten the image" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; // FIXME: toggle auto-crop? command = Magick.command [ "\"%s\"", "-deskew", "\"" ++ print threshold.value ++ "%%\"", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_despeckle_item = class Menuaction "_Despeckle" "reduce the speckles" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-despeckle", "\"%s\"" ]; _result = Magick.system command x; } } Magick_distort_item = class Menuaction "_Distort" "distort using a method and arguments" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; distort = Magick.distort_widget; args = String "Arguments" "1,0"; isPlus = Toggle "Extend to show entire image" false; command = Magick.command [ "\"%s\"", virtpixback._flag, (if isPlus then "+" else "-") ++ "distort", distort._flag, args.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_draw_item = class Menuaction "_Draw" "annotate with one or more graphic primitives" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; command = Magick.command [ "\"%s\"", foreground._flag, "-draw", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_edge_item = class Menuaction "_Edge" "detect edges" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-edge", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_emboss_item = class Menuaction "_Emboss" "emboss" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-emboss", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_enhance_item = class Menuaction "_Enhance" "enhance a noisy image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-enhance", "\"%s\"" ]; _result = Magick.system command x; } } Magick_equalize_item = class Menuaction "_Equalize" "equalize the histogram" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-equalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_evaluate_item = class Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; operation = Magick.evaluate_widget; val = Expression "value" 5; isPc = Toggle "Value is percent" false; command = Magick.command [ "\"%s\"", channels._flag, operation._flag, print val.expr ++ if isPc then "%%" else "", "\"%s\"" ]; _result = Magick.system command x; } } Magick_extent_item = class Menuaction "_Extent" "set the image size and offset" { action x = class _result { _vislevel = 3; background = Magick.background_widget; compose = Magick.compose_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, background._flag, gravity._flag, "-extent", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_FlipFlopMenu_item = class Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { Magick_flip_item = class Menuaction "_Flip vertically" "mirror upside-down" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flip", "\"%s\"" ]; _result = Magick.system command x; } } Magick_flop_item = class Menuaction "_Flop horizontally" "mirror left-right" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flop", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transpose_item = class Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transpose +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transverse_item = class Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transverse +repage", "\"%s\"" ]; _result = Magick.system command x; } } } // end Magick_FlipFlopMenu_item Magick_floodfill_item = class Menuaction "_Floodfill" "recolour neighbours that match" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; fuzz = Magick.fuzz_widget; coordinate = Magick.coordinate_widget; // -draw "color x,y floodfill" command = Magick.command [ "\"%s\"", foreground._flag, "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-draw \" color", coordinate._flag, "floodfill \"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_frame_item = class Menuaction "_Frame" "surround with border or beveled frame" { action x = class _result { _vislevel = 3; border = Magick.bordercol_widget; compose = Magick.compose_widget; matte = Magick.mattecol_widget; geometry = Magick.FrameGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, border._flag, matte._flag, "-frame", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_function_item = class Menuaction "_Function" "evaluate a function on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; function = Magick.function_widget; // FIXME: explain values; use sensible defaults. values = String "values" "0,0,0,0"; command = Magick.command [ "\"%s\"", channels._flag, "-function", function._flag, values.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_fx_item = class Menuaction "_Fx" "apply a mathematical expression" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; interpolate = Magick.interpolate_widget; virtpixback = Magick.VirtualPixelBack_widget; args = String "Expression" "u*1/2"; command = Magick.command [ "\"%s\"", channels._flag, interpolate._flag, virtpixback._flag, "-fx", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_gamma_item = class Menuaction "_Gamma" "apply a gamma correction" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; gamma = Magick.gamma_widget; command = Magick.command [ "\"%s\"", channels._flag, "-gamma", print gamma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradient_item = class Menuaction "_Gradient" "apply a linear gradient" { action x = class _result { _vislevel = 3; colourA = Magick.generalcol_widget; colourB = Magick.generalcol_widget; position = Option "colourA is at" [ "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"] 0; _baryArg = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 = "dunno"; command = Magick.command [ "\"%s\"", "-sparse-color barycentric \"" ++ _baryArg ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradientCorn_item = class Menuaction "_Gradient corners" "apply a bilinear gradient between the corners" { action x = class _result { _vislevel = 3; colour_top_left = Magick.generalcol_widget; colour_top_right = Magick.generalcol_widget; colour_bottom_left = Magick.generalcol_widget; colour_bottom_right = Magick.generalcol_widget; command = Magick.command [ "\"%s\"", "-sparse-color bilinear \"" ++ "0,0," ++ colour_top_left._flag ++ ",%%[fx:w-1],0" ++ colour_top_right._flag ++ ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", "+depth", "\"%s\"" ]; _result = Magick.system command x; } } Magick_histogram_item = class Menuaction "_Histogram" "make a histogram image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-define histogram:unique-colors=false histogram:" ++ "\"%s\"" ]; _result = Magick.system command x; } } Magick_implode_item = class Menuaction "_Implode" "implode pixels about the center" { action x = class _result { _vislevel = 3; factor = Scale "factor" 0 20 1; interpolate = Magick.interpolate_widget; // FIXME: virtual-pixel? command = Magick.command [ "\"%s\"", interpolate._flag, "-implode", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_level_item = class Menuaction "_Level" "adjust the level of channels" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "black point" (-100) 200 0; wht = Scale "white point" (-100) 200 100; gam = Scale "gamma" 0 30 1; isPc = Toggle "Levels are percent" true; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level", "\"" ++ print blk.value ++ "," ++ print wht.value ++ (if isPc then "%%" else "") ++ "," ++ print gam.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_levelCols_item = class Menuaction "_Level colors" "adjust levels to given colours" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; colour_black = Magick.generalcol_widget; colour_white = Magick.generalcol_widget; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level-colors", "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_linearStretch_item = class Menuaction "_Linear stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", channels._flag, "-linear-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_magnify_item = class Menuaction "_Magnify" "double the size of the image with pixel art scaling" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-magnify", "\"%s\"" ]; _result = Magick.system command x; } } Magick_modulate_item = class Menuaction "_Modulate" "modulate brightness, saturation and hue" { action x = class _result { _vislevel = 3; modcolsp = Magick.ModColSp_widget; bright = Scale "brightness" 0 200 100; sat = Scale "saturation" 0 200 100; hue = Scale "hue" 0 200 100; command = Magick.command [ "\"%s\"", modcolsp._flag, "-modulate", print bright.value ++ "," ++ print sat.value ++ "," ++ print hue.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_monochrome_item = class Menuaction "_Monochrome" "transform to black and white" { action x = class _result { _vislevel = 3; // FIXME: also intensity? intensity = Magick.intensity_widget; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; command = Magick.command [ "\"%s\"", intensity._flag, dither._flag, "-treedepth", print treedepth.expr, "-monochrome", "\"%s\"" ]; _result = Magick.system command x; } } Magick_morphology_item = class // See http://www.imagemagick.org/Usage/morphology/ Menuaction "_Morphology" "apply a morphological method" { action x = class _result { _vislevel = 3; method = Magick.morphmeth_widget; iter = Expression "Iterations (-1=repeat until done)" 1; kernel = Magick.kernel_widget; // FIXME: custom kernel eg "3x1+2+0:1,0,0" // width x height + offsx + offsy : {w*h values} // each value is 0.0 to 1.0 or "NaN" or "-" // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 // but // ring takes: radius1, radius2, scale // rectangle and comet take: width x height + offsx + offsy // blur takes: radius x sigma // FIXME: for now, simply allow any string input. kernel_arg = String "Kernel arguments" ""; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-morphology", method._flag ++ ":" ++ print iter.expr, kernel._flag ++ (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_negate_item = class Menuaction "_Negate" "negate" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-negate", "\"%s\"" ]; _result = Magick.system command x; } } Magick_addNoise_item = class Menuaction "_add Noise" "add noise" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; attenuate = Scale "attenuate" 0 1.0 1.0; noise = Magick.noise_widget; command = Magick.command [ "\"%s\"", channels._flag, "-attenuate", print attenuate.value, noise._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_normalize_item = class Menuaction "_Normalize" "normalize" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-normalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_opaque_item = class Menuaction "_Opaque" "change this colour to the fill colour" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; fill = Magick.foreground_widget; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", fill._flag, channels._flag, "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "opaque", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_paint_item = class Menuaction "_Paint" "simulate an oil painting" { action x = class _result { _vislevel = 3; rad = Expression "radius" 10; command = Magick.command [ "\"%s\"", "-paint", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } /*=== FIXME Bug; remove for now. Polaroid_item = class Menuaction "_Polaroid" "simulate a polaroid picture" { action x = class _result { _vislevel = 3; angle = Scale "angle" (-90) 90 20; command = Magick.command [ "\"%s\"", "-polaroid", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_posterize_item = class Menuaction "_Posterize" "reduce to (n) levels per channel" { action x = class _result { _vislevel = 3; levels = Expression "levels" 3; command = Magick.command [ "\"%s\"", "-posterize", print levels.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_raise_item = class Menuaction "_Raise" "lighten or darken image edges" { action x = class _result { _vislevel = 3; thk = Expression "Thickness" 3; command = Magick.command [ "\"%s\"", "-raise", print thk.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_resize_item = class Menuaction "_Resize" "resize to given width and height" { action x = class _result { _vislevel = 3; filter = Magick.filter_widget; type = Magick.ResizeType_widget; width = Scale "Width" 1 100 10; height = Scale "Height" 1 100 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", filter._flag, "-" ++ type._flag, "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "!") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_roll_item = class Menuaction "_Roll" "roll an image horizontally or vertically" { action x = class _result { _vislevel = 3; rollx = Expression "X" 3; rolly = Expression "Y" 3; command = Magick.command [ "\"%s\"", "-roll", (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotate_item = class Menuaction "_Rotate" "rotate" { action x = class _result { _vislevel = 3; angle = Scale "angle (degrees)" (-360) 360 20; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, "+distort", "SRT", print angle.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -segment, but cluster-threshold should be percentage of image area. Magick_sepia_item = class Menuaction "_Sepia tone" "simulate a sepia-toned photo" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; command = Magick.command [ "\"%s\"", "-sepia-tone", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shade_item = class Menuaction "_Shade" "shade with a distant light source" { action x = class _result { _vislevel = 3; azimuth = Scale "Azimuth (degrees)" (-360) 360 0; elevation = Scale "Elevation (degrees)" 0 90 45; command = Magick.command [ "\"%s\"", "-shade", print azimuth.value ++ "x" ++ print elevation.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_shadow_item = class Menuaction "_Shadow" "simulate a shadow" { action x = class _result { _vislevel = 3; shadowCol = Magick.generalcol_widget; opacity = Scale "Opacity (percent)" 0 100 75; sigma = Scale "Sigma" 0 30 2; // FIXME: make offsets a single widget? offsx = Scale "X-offset" (-20) 20 4; offsy = Scale "Y-offset" (-20) 20 4; arePc = Toggle "offsets are percentages" false; // FIXME: raw operation creates page offset, which vips dislikes. // So we take this futher, compositing with source. command = Magick.command [ "\"%s\"", "\"(\" +clone", "-background", "\"" ++ shadowCol._flag ++ "\"", "-shadow", concat [ "\"", print opacity.value, "x", print sigma.value, (if offsx.value >= 0 then "+" else ""), print offsx.value, (if offsy.value >= 0 then "+" else ""), print offsy.value, (if arePc then "%%" else ""), "\"" ], "\")\" +swap -background None -layers merge", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shave_item = class Menuaction "_Shave" "shave pixels from the edges" { action x = class _result { _vislevel = 3; width = Scale "Width" 0 50 10; height = Scale "Height" 0 50 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", "-shave", "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shear_item = class Menuaction "_Shear" "shear along the x-axis and/or y-axis" { action x = class _result { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-shear", print shearX.expr ++ "x" ++ print shearY.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sigmoid_item = class Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; contrast = Scale "contrast" 0 30 3; midpoint = Scale "mid-point (percent)" 0 100 50; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "sigmoidal-contrast", "\"" ++ print contrast.value ++ "x" ++ print midpoint.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_sketch_item = class Menuaction "_Sketch" "simulate a pencil sketch" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; command = Magick.command [ "\"%s\"", "-sketch", print radius.value ++ "x" ++ print sigma.value ++ (if angle >= 0 then ("+" ++ print angle.value) else ""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_solarize_item = class Menuaction "_Solarize" "negate all pixels above a threshold level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-solarize", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -sparse-color needs abitrary list of {x,y,colour}. Magick_splice_item = class Menuaction "_Splice" "splice a colour into the image" { action x = class _result { _vislevel = 3; background = Magick.background_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", background._flag, gravity._flag, "-splice", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_spread_item = class Menuaction "_Spread" "displace pixels by random amount" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; amount = Expression "Amount (pixels)" 5; command = Magick.command [ "\"%s\"", virtpixback._flag, "-spread", print amount.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_statistic_item = class Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { action x = class _result { _vislevel = 3; width = Expression "Width" 5; height = Expression "Height" 5; statisticType = Magick.StatType_widget; command = Magick.command [ "\"%s\"", "-statistic", statisticType._flag, print width.expr ++ "x" ++ print height.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = Magick.angle_widget; command = Magick.command [ "\"%s\"", "-swirl", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_thresholdMenu_item = class Menupullright "_Threshold" "make black or white" { Magick_threshold_item = class Menuaction "_Threshold" "apply black/white threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blackThreshold_item = class Menuaction "_Black threshold" "where below threshold set to black" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-black-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_whiteThreshold_item = class Menuaction "_White threshold" "where above threshold set to white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-white-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_latThreshold_item = class Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { action x = class _result { _vislevel = 3; width = Expression "Width" 10; height = Expression "Height" 10; offset = Scale "Offset (percent)" (-100) 100 0; // note: "-lat" doesn't respond to channels command = Magick.command [ "\"%s\"", "-lat", concat ["\"", print width.expr, "x", print height.expr, (if offset.value >= 0 then "+" else ""), print offset.value, "%%\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_randThreshold_item = class Menuaction "_Random Threshold" "between specified limits, apply random threshold" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; low = Scale "Low threshold" 0 100 10; high = Scale "High threshold" 0 100 90; isPc = Toggle "Thresholds are percent" true; command = Magick.command [ "\"%s\"", channels._flag, "-random-threshold", "\"" ++ print low.value ++ "x" ++ print high.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } } // end ThresholdMenu_item // Note: alternatives include: // convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif Magick_tile_item = class Menuaction "_Tile" "fill given size with tiled image" { action x = class _result { _vislevel = 3; size = Magick.Size_widget; command = Magick.command [ size._flag, "tile:" ++ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_tint_item = class Menuaction "_Tint" "apply a tint" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; amount = Scale "amount (percent)" 0 100 50; command = Magick.command [ "\"%s\"", foreground._flag, "-tint", // snibgo note: although the amount is a percentage, it doesn't need "%" character. print amount.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_transparent_item = class Menuaction "_Transparent" "make this colour transparent" { action x = class _result { _vislevel = 3; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "transparent", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_trim_item = class Menuaction "_Trim" "trims away border" { action x = class _result { _vislevel = 3; fuzz = Magick.fuzz_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-trim +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_uniqueCols_item = class Menuaction "_Unique colours" "discard all but one of any pixel color" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-unique-colors", "\"%s\"" ]; _result = Magick.system command x; } } Magick_vignette_item = class Menuaction "_Vignette" "soften the edges in vignette style" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; rx = Scale "Rolloff x (percent)" 0 100 10; ry = Scale "Rolloff y (percent)" 0 100 10; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-vignette", print radius.value ++ "x" ++ print sigma.value ++ (if rx.value >= 0 then "+" else "") ++ print rx.value ++ (if ry.value >= 0 then "+" else "") ++ print ry.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_wave_item = class Menuaction "_Wave" "shear the columns into a sine wave" { action x = class _result { _vislevel = 3; amplitude = Scale "Amplitude (pixels)" 0 100 10; wavelength = Scale "Wavelength (pixels)" 0 100 10; command = Magick.command [ "\"%s\"", "-wave", print amplitude.value ++ "x" ++ print wavelength.value, "\"%s\"" ]; _result = Magick.system command x; } } ================================================ FILE: share/nip2/compat/8.6/Makefile.am ================================================ startdir = $(pkgdatadir)/compat/8.6 start_DATA = \ Colour.def \ _convert.def \ Filter.def \ _generate.def \ Histogram.def \ Image.def \ _joe_extra.def \ _joe_utilities.def \ _list.def \ _magick.def \ Magick.def \ Math.def \ Matrix.def \ _Object.def \ Object.def \ _predicate.def \ _stdenv.def \ Tasks.def \ _types.def \ Widgets.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/compat/8.6/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/compat/8.6/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/compat/8.6/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/compat/8.6/Preferences.ws ================================================ ================================================ FILE: share/nip2/compat/8.6/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Kernel_linear xfactor yfactor scale_im; _offset_im = resize Kernel_linear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/compat/8.6/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/compat/8.6/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/compat/8.6/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make a Vector ... works for Vector/Image/Real, plus image/real */ to_vector x = to_vector x.expr, is_Expression x = x, is_Vector x = oo_unary_function to_vector_op x, is_class x = tov x { to_vector_op = Operator "to_vector" tov Operator_type.COMPOUND false; tov x = Vector (itov x), is_image x = Vector [x], is_real x = Vector x, is_real_list x = Vector x?0, is_matrix x && len x == 1 = Vector (transpose x)?0, is_matrix x && len x?0 == 1 = error (_ "bad arguments to " ++ "to_vector"); itov i = v, is_image i = error (_ "not image") { m = im_vips2mask ((double) i); v = m.value?0, m.height == 1 = (transpose m.value)?0, m.width == 1 = error (_ "image is not 1xN or Nx1"); } } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart >= 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* The vips8 scRGB functions. */ im_sRGB2scRGB in = out { [out] = vips_call "sRGB2scRGB" [in] []; } im_scRGB2sRGB in = out { [out] = vips_call "scRGB2sRGB" [in] []; } im_scRGB2XYZ in = out { [out] = vips_call "scRGB2XYZ" [in] []; } im_XYZ2scRGB in = out { [out] = vips_call "XYZ2scRGB" [in] []; } /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, scRGB, im_sRGB2scRGB @ im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, scRGB, im_XYZ2scRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, scRGB, im_XYZ2scRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, scRGB, im_XYZ2scRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, scRGB, im_sRGB2scRGB @ im_clip], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_Lab2LabS @ im_sRGB2Lab @ im_clip], [scRGB, B_W, im_sRGB2mono @ im_scRGB2sRGB], [scRGB, XYZ, im_scRGB2XYZ], [scRGB, YXY, im_XYZ2Yxy @ im_scRGB2XYZ], [scRGB, LAB, im_XYZ2Lab @ im_scRGB2XYZ], [scRGB, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_scRGB2XYZ], [scRGB, UCS, im_XYZ2UCS @ im_scRGB2XYZ], [scRGB, RGB, im_XYZ2disp @ im_scRGB2XYZ], [scRGB, sRGB, im_scRGB2sRGB], [scRGB, scRGB, image_set_type scRGB], [scRGB, RGB16, image_set_type RGB16 @ im_8216 @ im_scRGB2sRGB], [scRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono @ im_scRGB2sRGB], [scRGB, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_scRGB2XYZ], [scRGB, LABS, im_Lab2LabS @ im_XYZ2Lab @ im_scRGB2XYZ], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, scRGB, im_sRGB2scRGB], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [RGB16, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, scRGB, im_sRGB2scRGB @ im_mono2sRGB], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabS2Lab @ im_clip2s], [LABS, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LabS2Lab @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; scRGB = 28; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; /* Return $PATH, reformatted as [["comp1", "comp2"], ["comp1", "comp2"] ..] */ system_search_path = [vipsbin] ++ map path_parse (split (equal path_sep) (expand "$PATH")) { /* On some platforms we ship vips with a few extra progs. Search * $VIPSHOME/bin first. */ vipsbin = path_parse (expand "$VIPSHOME") ++ ["bin"]; path_sep = ':', expand "$SEP" == "/" = ';'; } /* Search $PATH for the first occurence of name, or "". */ search_for name = hits?0, hits != [] = "" { exe_name = name ++ expand "$EXEEXT"; form_path p = path_absolute (p ++ [exe_name]); paths = map form_path system_search_path; hits = dropwhile (equal []) (map search paths); } /* Search $PATH for the first occurence of name, error on failure. */ search_for_error name = path, path != "" = error (exe_name ++ " not found on your search path. " ++ "Check you have installed the program and it is on your PATH.") { exe_name = name ++ expand "$EXEEXT"; path = search_for name; } ================================================ FILE: share/nip2/compat/8.6/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/compat/8.6/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } Fill_item = class Menuaction "_Fill" "fill zero pixels with the nearest non-zero" { action x = class Image _result { _vislevel = 3; distance = Image _distance; [_result, _distance] = vips_call "fill_nearest" [x.value] [ "distance" => true ]; } } fill_nearest x = oo_unary_function nearest_op x, is_class x = near x, is_image x = error (_ "bad arguments to " ++ "fill_nearest") { nearest_op = Operator "fill_nearest" fill_nearest Operator_type.COMPOUND_REWRAP false; near x = [out, distance] { [out, distance] = vips_call "fill_nearest" [x] [ "distance" => true ]; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Kernel_linear f1 1 b1 {b1 = resize Kernel_linear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/compat/8.6/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/compat/8.6/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/compat/8.6/_magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate _magick.def Add 0-ary and 2-ary system Put utility funcs into a Magick class 11-Apr-2014 snibgo Added VirtualPixelBack for cases where background is only relevant when VP=Background 17-Apr-2014 snibgo Many small changes. 2-May-2014 jcupitt Added Magick.version 30-June-2014 Put single-quotes around command exe to help win 1-July-2014 Automatically fall back to gm if we can't find convert 17-July-2014 better GM support Last update: 17-July-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ /* Put these in a class to avoid filling the main namespace with IM stuff. */ Magick = class { // first gm on path, or "" gm_path = search_for "gm"; // first convert on $PATH, or "" // we check for the convert we ship first convert_path = vips_convert, vips_convert != "" = search_for "convert" { // the convert we ship with the vips binary on some platforms, or "" vips_convert = search (path_absolute convert) { vipshome = path_parse (expand "$VIPSHOME"); convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; } } use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; // Are we in GM or IM mode? use_gm = true, use_gm_pref && gm_path != "" = false, !use_gm_pref && convert_path != "" = false, convert_path != "" = true, gm_path != "" = error "neither IM nor GM executable found"; command_path = gm_path, use_gm = convert_path; // try to get the version as eg. [6, 7, 7, 10] // GM versions are smaller, typically [1, 3, 18] version = map parse_int (split (member ".-") version_string) { [output] = vips_call "system" ["'" ++ command_path ++ "' -version"] [$log=>true]; version_string = (split (equal ' ') output)?1, use_gm = (split (equal ' ') output)?2; } // make a command-line ... args is a [str] we join with spaces command args = "'" ++ command_path ++ "' " ++ join_sep " " args' { args' = ["convert"] ++ args, use_gm = args; } // capabilities ... different versions support different features, we // turn features on and off based on these // would probably be better to test for caps somehow has_intensity = false, use_gm = version?0 > 6 || version?1 > 7; has_channel = false, use_gm = version?0 > 6 || version?1 > 7; system0 cmd = system_image0 cmd; system cmd x = map_unary (system_image cmd) x; system2 cmd x y = map_binary (system_image2 cmd) x y; system3 cmd x y z = map_trinary (system_image3 cmd) x y z; radius_widget = Scale "Radius" 0 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle (degrees)" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; gamma_widget = Scale "Gamma" 0 10 1; colors_widget = Scale "Colors" 1 10 3; resize_widget = Scale "Resize (percent)" 0 500 100; fuzz_widget = Scale "Fuzz (percent)" 0 100 0; blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; // a colour with no enclosing quotes ... use this if we know there are // some quotes at an outer level print_colour_nq triple = concat ["#", concat (map fmt triple)] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } // we need the quotes because # is the comment character in *nix print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; GeneralCol triple = class Colour "sRGB" triple { _flag = print_colour_nq triple; Colour_edit space triple = this.GeneralCol triple; } generalcol_widget = GeneralCol [0, 0, 0]; Background triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" false; _flag = "-background " ++ if isNone then "None" else print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Bordercol triple = class Colour "sRGB" triple { _flag = "-bordercolor " ++ print_colour triple; Colour_edit space triple = this.Bordercol triple; } bordercol_widget = Bordercol [0, 0, 0]; Mattecol triple = class Colour "sRGB" triple { _flag = "-mattecolor " ++ print_colour triple; Colour_edit space triple = this.Mattecol triple; } mattecol_widget = Mattecol [189, 189, 189]; // FIXME: Undercolour, like many others, can have alpha channel. // How does user input this? With a slider? Undercol triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" true; _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); Colour_edit space triple = this.Undercol triple; } undercol_widget = Undercol [0, 0, 0]; changeCol_widget = class { _vislevel = 3; colour = GeneralCol [0, 0, 0]; fuzz = fuzz_widget; nonMatch = Toggle "change non-matching colours" false; } Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Remove", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Builtin builtin = class Option_string "Builtin" [ // See http://www.imagemagick.org/script/formats.php "rose:", "logo:", "wizard:", "granite:", "netscape:" ] builtin { _flag = builtin; Option_edit caption labels value = this.Builtin labels?value; } builtin_widget = Builtin "rose:"; channels_widget = class { // FIXME? Can we grey-out alpha when we have no alpha channel, // show CMY(K) instead of RGB(K) etc? // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. ChanR valueR = class Toggle "Red" valueR { _flag = "R", valueR = ""; Toggle_edit caption valueR = this.ChanR valueR; } channelR = ChanR true; ChanG valueG = class Toggle "Green" valueG { _flag = "G", valueG = ""; Toggle_edit caption valueG = this.ChanG valueG; } channelG = ChanG true; ChanB valueB = class Toggle "Blue" valueB { _flag = "B", valueB = ""; Toggle_edit caption valueB = this.ChanB valueB; } channelB = ChanB true; ChanK valueK = class Toggle "Black" valueK { _flag = "K", valueK = ""; Toggle_edit caption valueK = this.ChanK valueK; } channelK = ChanK true; ChanA valueA = class Toggle "Alpha" valueA { _flag = "A", valueA = ""; Toggle_edit caption valueA = this.ChanA valueA; } channelA = ChanA false; ChanSy valueSy = class Toggle "Sync" valueSy { _flag = ",sync", valueSy = ""; Toggle_edit caption valueSy = this.ChanSy valueSy; } channelSy = ChanSy true; _rgbka = concat [channelR._flag, channelG._flag, channelB._flag, channelK._flag, channelA._flag ]; _flag = "", _rgbka == "" || !has_channel = concat [ "-channel ", _rgbka, channelSy._flag ]; } ch_widget = channels_widget; Colorspace colsp = class Option_string "Colorspace" [ "CIELab", "CMY", "CMYK", "Gray", "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "Lab", "LCH", "LCHab", "LCHuv", "LMS", "Log", "Luv", "OHTA", "Rec601Luma", "Rec601YCbCr", "Rec709Luma", "Rec709YCbCr", "RGB", "scRGB", "sRGB", "Transparent", "XYZ", "YCbCr", "YDbDr", "YCC", "YIQ", "YPbPr", "YUV" ] colsp { _flag = colsp; Option_edit caption labels value = this.Colorspace labels?value; } colorspace_widget = Colorspace "sRGB"; Compose comp = class Option_string "Compose method" [ "Atop", "Blend", "Blur", "Bumpmap", "ChangeMask", "Clear", "ColorBurn", "ColorDodge", "Colorize", "CopyBlack", "CopyBlue", "CopyCyan", "CopyGreen", "Copy", "CopyMagenta", "CopyOpacity", "CopyRed", "CopyYellow", "Darken", "DarkenIntensity", "DivideDst", "DivideSrc", "Dst", "Difference", "Displace", "Dissolve", "Distort", "DstAtop", "DstIn", "DstOut", "DstOver", "Exclusion", "HardLight", "Hue", "In", "Lighten", "LightenIntensity", "LinearBurn", "LinearDodge", "LinearLight", "Luminize", "Mathematics", "MinusDst", "MinusSrc", "Modulate", "ModulusAdd", "ModulusSubtract", "Multiply", "None", "Out", "Overlay", "Over", "PegtopLight", "PinLight", "Plus", "Replace", "Saturate", "Screen", "SoftLight", "Src", "SrcAtop", "SrcIn", "SrcOut", "SrcOver", "VividLight", "Xor" ] comp { _flag = "-compose " ++ comp; Option_edit caption labels value = this.Compose labels?value; } compose_widget = Compose "Over"; // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack coordinate_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; _flag = concat [print x.expr, ",", print y.expr]; }; Distort distort = class Option_string "Distort" [ "Affine", "AffineProjection", "ScaleRotateTranslate", "SRT", "Perspective", "PerspectiveProjection", "BilinearForward", "BilinearReverse", "Polynomial", "Arc", "Polar", "DePolar", "Barrel", "BarrelInverse", "Shepards", "Resize" ] distort { _flag = distort; Option_edit caption labels value = this.Distort labels?value; } distort_widget = Distort "SRT"; Dither dither = class Option_string "Dither" [ "None", "FloydSteinberg", "Riemersma" ] dither { _flag = "-dither " ++ dither; Option_edit caption labels value = this.Dither labels?value; } dither_widget = Dither "FloydSteinberg"; Evaluate eval = class Option_string "Evaluate operation" [ "Abs", "Add", "AddModulus", "And", "Cos", "Cosine", "Divide", "Exp", "Exponential", "GaussianNoise", "ImpulseNoise", "LaplacianNoise", "LeftShift", "Log", "Max", "Mean", "Median", "Min", "MultiplicativeNoise", "Multiply", "Or", "PoissonNoise", "Pow", "RightShift", "Set", "Sin", "Sine", "Subtract", "Sum", "Threshold", "ThresholdBlack", "ThresholdWhite", "UniformNoise", "Xor" ] eval { _flag = "-evaluate " ++ eval; Option_edit caption labels value = this.Evaluate labels?value; } evaluate_widget = Evaluate "Add"; Filter filt = class Option_string "Filter" [ "default", "Bartlett", "Blackman", "Bohman", "Box", "Catrom", "Cosine", "Cubic", "Gaussian", "Hamming", "Hann", "Hermite", "Jinc", "Kaiser", "Lagrange", "Lanczos", "Lanczos2", "Lanczos2Sharp", "LanczosRadius", "LanczosSharp", "Mitchell", "Parzen", "Point", "Quadratic", "Robidoux", "RobidouxSharp", "Sinc", "SincFast", "Spline", "Triangle", "Welch" ] filt { _flag = if filt == "default" then "" else "-filter " ++ filt; Option_edit caption labels value = this.Filter labels?value; } filter_widget = Filter "default"; Function func = class Option_string "Function" [ "Polynomial", "Sinusoid", "Arcsin", "Arctan" ] func { _flag = func; Option_edit caption labels value = this.Function labels?value; } function_widget = Function "Polynomial"; // "Polynomial (a[n], a[n-1], ... a[1], a[0])", // "Sinusoid (freq, phase, amp, bias)", // "Arcsin (width, centre, range, bias)", // "Arctan (slope, centre, range, bias)" Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; ImageType imagetype = class Option_string "Image type" [ "Bilevel", "ColorSeparation", "ColorSeparationAlpha", "ColorSeparationMatte", "Grayscale", "GrayscaleAlpha", "GrayscaleMatte", "Optimize", "Palette", "PaletteBilevelAlpha", "PaletteBilevelMatte", "PaletteAlpha", "PaletteMatte", "TrueColorAlpha", "TrueColorMatte", "TrueColor" ] imagetype { _flag = "-type " ++ imagetype; Option_edit caption labels value = this.ImageType labels?value; } imagetype_widget = ImageType "TrueColor"; Intensity intensity = class Option_string "Intensity (gray conversion)" [ "Average", "Brightness", "Lightness", "MS", "Rec601Luma", "Rec601Luminance", "Rec709Luma", "Rec709Luminance", "RMS" ] intensity { _flag = "-intensity " ++ intensity, has_intensity = ""; Option_edit caption labels value = this.Intensity labels?value; } intensity_widget = Intensity "Rec709Luminance"; Interpolate interp = class Option_string "Interpolate" [ "default", "Average", "Average4", "Average9", "Average16", "Background", "Bilinear", "Blend", "Integer", "Mesh", "Nearest", "NearestNeighbor", "Spline" ] interp { _flag = if interp == "default" then "" else "-interpolate " ++ interp; Option_edit caption labels value = this.Interpolate labels?value; } interpolate_widget = Interpolate "default"; Kernel kernel = class Option_string "Kernel" [ "Unity", "Gaussian", "DoG", "LoG", "Blur", "Comet", "Binomial", "Laplacian", "Sobel", "FreiChen", "Roberts", "Prewitt", "Compass", "Kirsch", "Diamond", "Square", "Rectangle", "Disk", "Octagon", "Plus", "Cross", "Ring", "Peaks", "Edges", "Corners", "Diagonals", "LineEnds", "LineJunctions", "Ridges", "ConvexHull", "ThinSe", "Skeleton", "Chebyshev", "Manhattan", "Octagonal", "Euclidean" // FIXME: custom kernel ] kernel { _flag = kernel; Option_edit caption labels value = this.Kernel labels?value; } kernel_widget = Kernel "Unity"; ModColSp msp = class Option_string "modulate colorspace" [ "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "LCH" ] msp { _flag = "-set option:modulate:colorspace " ++ msp; Option_edit caption labels value = this.ModColSp labels?value; } ModColSp_widget = ModColSp "HSL"; MorphMeth morph = class Option_string "Method" [ "Correlate", "Convolve", "Dilate", "Erode", "Close", "Open", "DilateIntensity", "ErodeIntensity", "CloseIntensity", "OpenIntensity", "Smooth", "EdgeOut", "EdgeIn", "Edge", "TopHat", "BottomHat", "HitAndMiss", "Thinning", "Thicken", "Distance", "IterativeDistance" ] morph { _flag = morph; Option_edit caption labels value = this.MorphMeth labels?value; } morphmeth_widget = MorphMeth "Dilate"; Noise noise = class Option_string "Noise" [ "Gaussian", "Impulse", "Laplacian", "Multiplicative", "Poisson", "Random", "Uniform" ] noise { _flag = "+noise " ++ noise; Option_edit caption labels value = this.Noise labels?value; } noise_widget = Noise "Gaussian"; Pattern pattern = class Option_string "Noise" [ // See http://www.imagemagick.org/script/formats.php "bricks", "checkerboard", "circles", "crosshatch", "crosshatch30", "crosshatch45", "gray0", "gray5", "gray10", "gray15", "gray20", "gray25", "gray30", "gray35", "gray40", "gray45", "gray50", "gray55", "gray60", "gray65", "gray70", "gray75", "gray80", "gray85", "gray90", "gray95", "gray100", "hexagons", "horizontal", "horizontal2", "horizontal3", "horizontalsaw", "hs_bdiagonal", "hs_cross", "hs_diagcross", "hs_fdiagonal", "hs_horizontal", "hs_vertical", "left30", "left45", "leftshingle", "octagons", "right30", "right45", "rightshingle", "smallfishscales", "vertical", "vertical2", "vertical3", "verticalbricks", "verticalleftshingle", "verticalrightshingle", "verticalsaw" ] pattern { _flag = "pattern:" ++ pattern; Option_edit caption labels value = this.Pattern labels?value; } pattern_widget = Pattern "bricks"; ResizeType resizet = class Option_string "Resize type" [ "resize", "scale", "sample", "adaptive-resize" ] resizet { _flag = resizet; Option_edit caption labels value = this.ResizeType labels?value; } ResizeType_widget = ResizeType "resize"; Size_widget = class { _vislevel = 3; width = Expression "Width (pixels)" 64; height = Expression "Height (pixels)" 64; _flag = "-size " ++ print width.expr ++ "x" ++ print height.expr; }; StatType statt = class Option_string "Statistic type" [ "Gradient", "Maximum", "Mean", "Median", "Minimum", "Mode", "Nonpeak", "StandardDeviation" ] statt { _flag = statt; Option_edit caption labels value = this.StatType labels?value; } StatType_widget = StatType "Mean"; VirtualPixel vp = class Option_string "Virtual pixel" [ "Background", "Black", "CheckerTile", "Dither", "Edge", "Gray", "HorizontalTile", "HorizontalTileEdge", "Mirror", "None", "Random", "Tile", "Transparent", "VerticalTile", "VerticalTileEdge", "White" ] vp { _flag = "-virtual-pixel " ++ vp; _isBackground = (vp == "Background"); Option_edit caption labels value = this.VirtualPixel labels?value; } VirtualPixel_widget = VirtualPixel "Edge"; VirtualPixelBack_widget = class { virtpix = Magick.VirtualPixel_widget; background = Magick.background_widget; _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") ++ virtpix._flag; } Geometry_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; AnnotGeometry_widget = class { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print shearX.expr, "x", print shearY.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; OffsetGeometry_widget = class { _vislevel = 3; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; WhxyGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; FrameGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; outbev = Expression "Outer bevel thickness" 0; inbev = Expression "Inner bevel thickness" 0; _flag = concat [print x.expr, "x", print y.expr, format outbev, format inbev] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } } ================================================ FILE: share/nip2/compat/8.6/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.scRGB, coding == Image_coding.RAD = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.scRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/compat/8.6/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_tile_cache_random x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } decode im = oo_unary_function decode_op im, is_class im = decode_im im, is_image im = error (_ "bad arguments to " ++ "decode") { decode_op = Operator "decode" decode Operator_type.COMPOUND_REWRAP false; decode_im im = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK = im_rad2float im, get_coding im == Image_coding.RAD = im; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; image' = decode image; patches = map (\p extract_area p?0 p?1 sample_width sample_height image') cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed mode index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) (hist_find_indexed mode)) Operator_type.COMPOUND false; indexed index value = out { [out] = vips_call "hist_find_indexed" [value, index] [ "combine" => mode ]; } } hist_entropy x = oo_unary_function hist_entropy_op x, is_class x = entropy x, is_image x = error (_ "bad arguments to " ++ "hist_entropy") { hist_entropy_op = Operator "hist_entropy" hist_entropy Operator_type.COMPOUND_REWRAP false; entropy x = out { [out] = vips_call "hist_entropy" [x] [ ]; } } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h l image = oo_unary_function hist_equalize_local_op image, is_class image = out, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h l) Operator_type.COMPOUND_REWRAP false; [out] = vips_call "hist_local" [image, to_real w, to_real h] [$max_slope => to_real l]; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } reduce kernel xshr yshr image = oo_unary_function reduce_op image, is_class image = reduce_im image, is_image image = error (_ "bad arguments to " ++ "reduce") { reduce_op = Operator "reduce" reduce_im Operator_type.COMPOUND_REWRAP false; reduce_im im = out { [out] = vips_call "reduce" [im, xshr, yshr] [$kernel => kernel.value]; } } similarity interpolate scale angle image = oo_unary_function similarity_op image, is_class image = similarity_im image, is_image image = error (_ "bad arguments to " ++ "similarity") { similarity_op = Operator "similarity" similarity_im Operator_type.COMPOUND_REWRAP false; similarity_im im = out { [out] = vips_call "similarity" [im] [ $interpolate => interpolate.value, $scale => scale, $angle => angle ]; } } resize kernel xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; is_nn = kernel.type == Kernel_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn // everything else ... we just pass on to vips_resize() = vips_resize kernel xfac' yfac' im { vips_resize kernel hscale vscale im = out { [out] = vips_call "resize" [im, hscale] [$vscale => vscale, $kernel => kernel.value]; } } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C * * Also calculate R2, see eg.: * https://en.wikipedia.org/wiki/Coefficient_of_determination */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } ss = len xes; sx = sum xes; sy = sum yes; my = sy / ss; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; my = sy / len xes; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* One image in, one out. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } /* Two images in, one out. */ system_image2 command x1 x2 = oo_binary_function system_image2_op x1 x2, is_class x1 = oo_binary'_function system_image2_op x1 x2, is_class x2 = system x1 x2, is_image x1 && is_image x2 = error (_ "bad arguments to " ++ "system_image2") { system_image2_op = Operator "system_image2" (system_image2 command) Operator_type.COMPOUND_REWRAP false; system x1 x2 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Three images in, one out. */ system_image3 command x1 x2 x3 = oo_binary_function system_image2_op x2 x3, is_class x2 = oo_binary'_function system_image2_op x2 x3, is_class x3 = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 = error (_ "bad arguments to " ++ "system_image3") { system_image2_op = Operator "system_image2" (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; system x1 x2 x3 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2, x3], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Zero images in, one out. */ system_image0 command = Image image_out { [image_out] = vips_call "system" [command] [ $out => true, $out_format => "%s.tif" ]; } hough_line w h x = oo_unary_function hough_line_op x, is_class x = hline (to_real w) (to_real h) x, is_image x = error (_ "bad arguments to " ++ "hough_line") { hough_line_op = Operator "hough_line" (hough_line w h) Operator_type.COMPOUND_REWRAP false; hline w h x = pspace { [pspace] = vips_call "hough_line" [x] [ $width => w, $height => h ]; } } hough_circle s mn mx x = oo_unary_function hough_circle_op x, is_class x = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x = error (_ "bad arguments to " ++ "hough_circle") { hough_circle_op = Operator "hough_circle" (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; hcircle s mn mx x = pspace { [pspace] = vips_call "hough_circle" [x] [ $scale => s, $min_radius => mn, $max_radius => mx ]; } } mapim interp ind in = oo_binary_function mapim_op ind in, is_class ind = oo_binary'_function mapim_op ind in, is_class in = mapim_fn ind in, is_image ind && is_image in = error (_ "bad arguments to " ++ "mapim") { mapim_op = Operator "mapim" (mapim interp) Operator_type.COMPOUND_REWRAP false; mapim_fn ind im = out { [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; } } perlin cell width height = Image im { [im] = vips_call "perlin" [to_real width, to_real height] [ $cell_size => to_real cell ]; } worley cell width height = Image im { [im] = vips_call "worley" [to_real width, to_real height] [ $cell_size => to_real cell ]; } gaussnoise width height mean sigma = im { [im] = vips_call "gaussnoise" [to_real width, to_real height] [ $mean => to_real mean, $sigma => to_real sigma ]; } flattenimage bg x = oo_unary_function flatten_op x, is_class x = flt (to_vector bg) x, is_image x = error (_ "bad arguments to " ++ "flattenimage") { flatten_op = Operator "flatten" (flattenimage bg) Operator_type.COMPOUND_REWRAP false; flt bg x = out { [out] = vips_call "flatten" [x] [ $background => bg.value ]; } } premultiply x = oo_unary_function premultiply_op x, is_class x = prem x, is_image x = error (_ "bad arguments to " ++ "premultiply") { premultiply_op = Operator "premultiply" premultiply Operator_type.COMPOUND_REWRAP false; prem x = out { [out] = vips_call "premultiply" [x] [ ]; } } unpremultiply x = oo_unary_function unpremultiply_op x, is_class x = unprem x, is_image x = error (_ "bad arguments to " ++ "unpremultiply") { unpremultiply_op = Operator "unpremultiply" unpremultiply Operator_type.COMPOUND_REWRAP false; unprem x = out { [out] = vips_call "unpremultiply" [x] [ ]; } } hist_entropy x = oo_unary_function hist_entropy_op x, is_class x = entropy x, is_image x = error (_ "bad arguments to " ++ "hist_entropy") { hist_entropy_op = Operator "hist_entropy" hist_entropy Operator_type.COMPOUND_REWRAP false; entropy x = out { [out] = vips_call "hist_entropy" [x] [ ]; } } ================================================ FILE: share/nip2/compat/8.6/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; scRGB = 28; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY, $scRGB => scRGB ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options are generated from this, so match the order to the order in * the Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $scRGB => scRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $scRGB => scRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Kernel_type = class { NEAREST_NEIGHBOUR = 0; LINEAR = 1; CUBIC = 2; LANCZOS2 = 3; LANCZOS3 = 4; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map kernel numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Linear", _ "Cubic", _ "Lanczos, two lobes", _ "Lanczos, three lobes" ]; /* And to vips enum nicknames. */ types = [ "nearest", "linear", "cubic", "lanczos2", "lanczos3" ]; } Kernel type = class { value = Kernel_type.types?type; } Kernel_linear = Kernel Kernel_type.LINEAR; Kernel_picker default = class Kernel kernel.value { _vislevel = 2; kernel = Option "Kernel" Kernel_type.descriptions default; } Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; to_image dpi = extract_bands 0 3 (graph_export_image (to_real dpi) this); } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } Blend_type = class { CLEAR = 0; SOURCE = 1; OVER = 2; IN = 3; OUT = 4; ATOP = 5; DEST = 6; DEST_OVER = 7; DEST_IN = 8; DEST_OUT = 9; DEST_ATOP = 10; XOR = 11; ADD = 12; SATURATE = 13; MULTIPLY = 14; SCREEN = 15; OVERLAY = 16; DARKEN = 17; LIGHTEN = 18; COLOUR_DODGE = 19; COLOUR_BURN = 20; HARD_LIGHT = 21; SOFT_LIGHT = 22; DIFFERENCE = 23; EXCLUSION = 24; /* Table to map blend numbers to descriptive strings */ descriptions = [ _ "Clear", _ "Source", _ "Over", _ "In", _ "Out", _ "Atop", _ "Dest", _ "Dest over", _ "Dest in", _ "Dest out", _ "Dest atop", _ "Xor", _ "Add", _ "Saturate", _ "Multiply", _ "Screen", _ "Overlay", _ "Darken", _ "Lighten", _ "Colour dodge", _ "Colour burn", _ "Hard light", _ "Soft light", _ "Difference", _ "Exclusion" ]; /* And to vips enum nicknames. */ types = Enum [ $clear => "clear", $source => "source", $over => "over", $in => "in", $out => "out", $atop => "atop", $dest => "dest", $dest_over => "dest_over", $dest_in => "dest_in", $dest_out => "dest_out", $dest_atop => "dest_atop", $xor => "xor", $add => "add", $saturate => "saturate", $multiply => "multiply", $screen => "screen", $overlay => "overlay", $darken => "darken", $lighten => "lighten", $colour_dodge => "colour_dodge", $colour_burn => "colour_burn", $hard_light => "hard_light", $soft_light => "soft_light", $difference => "difference", $exclusion => "exclusion" ]; } Blend type = class { value = Blend_type.types?type; } Blend_over = Blend Blend_type.OVER; Blend_picker default = class Blend blend.value { _vislevel = 2; blend = Option "Blend" Blend_type.descriptions default; } Combine_type = class { MAX = 0; SUM = 1; MIN = 2; enum = Enum [ _ "Maximum" => MAX, _ "Sum" => SUM, _ "Minimum" => MIN ]; } Combine type = class { value = Combine_type.enum.names?type; } Combine_sum = Combine Combine_type.SUM; Combine_picker default = Option "Combine" Combine_type.enum.names default; ================================================ FILE: share/nip2/compat/Makefile.am ================================================ SUBDIRS = 7.8 7.9 7.10 7.12 7.14 7.16 7.24 7.26 7.28 7.38 7.40 8.2 8.3 \ 8.4 8.5 8.6 ================================================ FILE: share/nip2/compat/_compat.def ================================================ // compatibility with nip-7.8 _colour_conv2 from to = _colour_conv to @ _colour_set from; Mono_to = class { Mono x = _colour_conv2 Image_type.B_W Image_type.B_W x; XYZ x = _colour_conv2 Image_type.B_W Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.B_W Image_type.YXY x; Lab x = _colour_conv2 Image_type.B_W Image_type.LAB x; LCh x = _colour_conv2 Image_type.B_W Image_type.LCH x; UCS x = _colour_conv2 Image_type.B_W Image_type.UCS x; RGB x = _colour_conv2 Image_type.B_W Image_type.RGB x; sRGB x = _colour_conv2 Image_type.B_W Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.B_W Image_type.LABQ x; LabS x = _colour_conv2 Image_type.B_W Image_type.LABS x; } XYZ_to = class { Mono x = _colour_conv2 Image_type.XYZ Image_type.B_W x; XYZ x = _colour_conv2 Image_type.XYZ Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.XYZ Image_type.YXY x; Lab x = _colour_conv2 Image_type.XYZ Image_type.LAB x; LCh x = _colour_conv2 Image_type.XYZ Image_type.LCH x; UCS x = _colour_conv2 Image_type.XYZ Image_type.UCS x; RGB x = _colour_conv2 Image_type.XYZ Image_type.RGB x; sRGB x = _colour_conv2 Image_type.XYZ Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.XYZ Image_type.LABQ x; LabS x = _colour_conv2 Image_type.XYZ Image_type.LABS x; } Yxy_to = class { Mono x = _colour_conv2 Image_type.YXY Image_type.B_W x; XYZ x = _colour_conv2 Image_type.YXY Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.YXY Image_type.YXY x; Lab x = _colour_conv2 Image_type.YXY Image_type.LAB x; LCh x = _colour_conv2 Image_type.YXY Image_type.LCH x; UCS x = _colour_conv2 Image_type.YXY Image_type.UCS x; RGB x = _colour_conv2 Image_type.YXY Image_type.RGB x; sRGB x = _colour_conv2 Image_type.YXY Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.YXY Image_type.LABQ x; LabS x = _colour_conv2 Image_type.YXY Image_type.LABS x; } Lab_to = class { Mono x = _colour_conv2 Image_type.LAB Image_type.B_W x; XYZ x = _colour_conv2 Image_type.LAB Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.LAB Image_type.YXY x; Lab x = _colour_conv2 Image_type.LAB Image_type.LAB x; LCh x = _colour_conv2 Image_type.LAB Image_type.LCH x; UCS x = _colour_conv2 Image_type.LAB Image_type.UCS x; RGB x = _colour_conv2 Image_type.LAB Image_type.RGB x; sRGB x = _colour_conv2 Image_type.LAB Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.LAB Image_type.LABQ x; LabS x = _colour_conv2 Image_type.LAB Image_type.LABS x; } LCh_to = class { Mono x = _colour_conv2 Image_type.LCH Image_type.B_W x; XYZ x = _colour_conv2 Image_type.LCH Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.LCH Image_type.YXY x; Lab x = _colour_conv2 Image_type.LCH Image_type.LAB x; LCh x = _colour_conv2 Image_type.LCH Image_type.LCH x; UCS x = _colour_conv2 Image_type.LCH Image_type.UCS x; RGB x = _colour_conv2 Image_type.LCH Image_type.RGB x; sRGB x = _colour_conv2 Image_type.LCH Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.LCH Image_type.LABQ x; LabS x = _colour_conv2 Image_type.LCH Image_type.LABS x; } UCS_to = class { Mono x = _colour_conv2 Image_type.UCS Image_type.B_W x; XYZ x = _colour_conv2 Image_type.UCS Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.UCS Image_type.YXY x; Lab x = _colour_conv2 Image_type.UCS Image_type.LAB x; LCh x = _colour_conv2 Image_type.UCS Image_type.LCH x; UCS x = _colour_conv2 Image_type.UCS Image_type.UCS x; RGB x = _colour_conv2 Image_type.UCS Image_type.RGB x; sRGB x = _colour_conv2 Image_type.UCS Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.UCS Image_type.LABQ x; LabS x = _colour_conv2 Image_type.UCS Image_type.LABS x; } RGB_to = class { Mono x = _colour_conv2 Image_type.RGB Image_type.B_W x; XYZ x = _colour_conv2 Image_type.RGB Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.RGB Image_type.YXY x; Lab x = _colour_conv2 Image_type.RGB Image_type.LAB x; LCh x = _colour_conv2 Image_type.RGB Image_type.LCH x; UCS x = _colour_conv2 Image_type.RGB Image_type.UCS x; RGB x = _colour_conv2 Image_type.RGB Image_type.RGB x; sRGB x = _colour_conv2 Image_type.RGB Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.RGB Image_type.LABQ x; LabS x = _colour_conv2 Image_type.RGB Image_type.LABS x; } sRGB_to = class { Mono x = _colour_conv2 Image_type.sRGB Image_type.B_W x; XYZ x = _colour_conv2 Image_type.sRGB Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.sRGB Image_type.YXY x; Lab x = _colour_conv2 Image_type.sRGB Image_type.LAB x; LCh x = _colour_conv2 Image_type.sRGB Image_type.LCH x; UCS x = _colour_conv2 Image_type.sRGB Image_type.UCS x; RGB x = _colour_conv2 Image_type.sRGB Image_type.RGB x; sRGB x = _colour_conv2 Image_type.sRGB Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.sRGB Image_type.LABQ x; LabS x = _colour_conv2 Image_type.sRGB Image_type.LABS x; } LabQ_to = class { Mono x = _colour_conv2 Image_type.LABQ Image_type.B_W x; XYZ x = _colour_conv2 Image_type.LABQ Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.LABQ Image_type.YXY x; Lab x = _colour_conv2 Image_type.LABQ Image_type.LAB x; LCh x = _colour_conv2 Image_type.LABQ Image_type.LCH x; UCS x = _colour_conv2 Image_type.LABQ Image_type.UCS x; RGB x = _colour_conv2 Image_type.LABQ Image_type.RGB x; sRGB x = _colour_conv2 Image_type.LABQ Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.LABQ Image_type.LABQ x; LabS x = _colour_conv2 Image_type.LABQ Image_type.LABS x; } LabS_to = class { Mono x = _colour_conv2 Image_type.LABS Image_type.B_W x; XYZ x = _colour_conv2 Image_type.LABS Image_type.XYZ x; Yxy x = _colour_conv2 Image_type.LABS Image_type.YXY x; Lab x = _colour_conv2 Image_type.LABS Image_type.LAB x; LCh x = _colour_conv2 Image_type.LABS Image_type.LCH x; UCS x = _colour_conv2 Image_type.LABS Image_type.UCS x; RGB x = _colour_conv2 Image_type.LABS Image_type.RGB x; sRGB x = _colour_conv2 Image_type.LABS Image_type.sRGB x; LabQ x = _colour_conv2 Image_type.LABS Image_type.LABQ x; LabS x = _colour_conv2 Image_type.LABS Image_type.LABS x; } // various renames Tint_mono_image = Tint_image; Matrix_from_colour_chart = Colour_chart_to_matrix; Colour_chart_from_matrix = Matrix_to_colour_chart; Colour_from_image = Image_to_colour; Image_from_colour = Colour_to_image; Convert_format_to = Convert_numeric_format_to; Insert a b = Insert_image b a; /* make a colour overlay of two mono images */ Overlay a b = class Image value { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.bands == 1 && b.bands == 1, "a.bands == 1 && b.bands == 1"] ]; _vislevel = 3; ap1 = Point_relative a 0.5 0.25; bp1 = Point_relative b 0.5 0.25; ap2 = Point_relative a 0.5 0.75; bp2 = Point_relative b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; colour = Option "Colour overlay as" [ "Green over Red", "Blue over Red", "Red over Green", "Red over Blue", "Blue over Green", "Green over Blue" ] 0; value = [(a' ++ b''' ++ black), (a' ++ black ++ b'''), (b''' ++ a' ++ black), (b''' ++ black ++ a'), (black ++ a' ++ b'''), (black ++ b''' ++ a')]?colour { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; black = image_new a.width a.height a.bands a.format a.coding a.type 0 0 0; } } /* apply a colour to an image */ Tint_image in = class _result { _vislevel = 3; tint = Colour "Lab" [50, 0, 0]; type = Option "Tint type" ["Colour filter", "Colour replacement"] 1; _result = map_unary apply_tint in { apply_tint in = result, type == 0 = fancy_result { // input image ... to L only in_lab = colour_transform_to Image_type.LAB in; in_l = in_lab?0; // make sure tint is LAB (might have been edited) tint_lab = colour_transform_to Image_type.LAB tint; // selected lab tint_l = tint_lab.value?0; tint_a = tint_lab.value?1; tint_b = tint_lab.value?2; // how much tint to apply .. maximum tint where image // L is equal to tint L, angle towards 0 tint up to // white and down to black mod = (100 - in_l) / (100 - tint_l), in_l > tint_l = in_l / tint_l; // tag as lab (we've probably lost the tag in all that // fiddling) lab = image_set_type Image_type.LAB (in_l ++ (mod * tint_a) ++ (mod * tint_b)); // don't convert back to the input colourspace if it's // mono .. converting back to mono will lose all // our colour fancy_result = lab, in.bands == 1 = colour_transform_to (get_type in) lab; // again, but just do a simple colour filter in_xyz = colour_transform_to Image_type.XYZ in; tint_xyz = colour_transform_to Image_type.XYZ tint; result_xyz = in_xyz * (tint_xyz / 100); result = result_xyz, in.bands == 1 = colour_transform_to (get_type in) result_xyz; } } } /* add an editable drop shadow to an image */ Drop_shadow x = map_unary shadow x { shadow image = class Image value { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; shadow_width = Slider 0 50 5; shadow_height = Slider 0 50 5; shadow_softness = Slider 0 20 5; use_mask = Toggle "Use mask to make shadow" false; mask_image = foldr1 bitwise_and (bandsplit (image > 128)); background_colour = 255; shadow_colour = 128; value = final { blur_size = shadow_softness.value * 2 + 1; // matrix we blur with to soften shadows mask_g = im_gauss_imask (blur_size / 3) 0.2; mask_g_line = mask_g.value?(mask_g.height / 2); mask_g_sum = foldr1 add mask_g_line; blur_matrix = Matrix_con mask_g_sum 0 [mask_g_line]; mask_size = mask_g.width; // size of final image we build final_width = image.width + 2 * mask_size + shadow_width.value; final_height = image.height + 2 * mask_size + shadow_height.value; // make a plain image mk_background colour = image_new final_width final_height image.bands image.format Image_coding.NOCODING image.type colour 0 0; // make a mask image ... place at (x,y) in the final // image mk_mask x y = im_insert black mask_image.value x y, use_mask = im_insert black white x y { black = image_new final_width final_height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 0 0 0; white = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; } // make the shadow mask image ... offset mask and // soften shadow_mask = mk_mask (mask_size + shadow_width.value) (mask_size + shadow_height.value); shadow_mask' = im_convsep shadow_mask blur_matrix; // make underlay ... use shadow mask to blend between // background colour and shadow colour background = mk_background background_colour; shadow = mk_background shadow_colour; underlay = im_blend shadow_mask' shadow background; // overlay ... place image at final position overlay = mk_background 0; overlay' = im_insert overlay image.value mask_size mask_size; // overlay mask overlay_mask = mk_mask mask_size mask_size; final = if overlay_mask then overlay' else underlay; } } } // renamed these Mosaic_translate = Mosaic_1point; Mosaic_force = class { Left_right = Mosaic_1point.Left_right_manual; Top_bottom = Mosaic_1point.Top_bottom_manual; } Mosaic_affine = Mosaic_2point; // Mark used to be called Point, and was relative to xoffset/yoffset // need to keep this so we can still edit old image mosaic workspaces is_Point x = is_instanceof "Point" x; check_Point = check_instance "Point"; Point image l t = class root.Mark image (l + image.xoffset) (t + image.yoffset) { Mark image l t = this.Point image (l - image.xoffset) (t - image.xoffset); } Point_relative image u v = Point image (image.width * u) (image.height * v); /* morph image colours in LAB space ... useful for tweaking colour for print */ Morph_for_print in = map_unary widget in { widget in = class Image value { _check_args = [ [in, "in", check_Image] ]; _vislevel = 3; L_scale = 1.15; L_offset = -4.2; ab_scale = Slider 1 1.5 1.15; a_offset = Slider (-10) 10 0; b_offset = Slider (-10) 10 5; grey_correction = Matrix_con 1 0 [ [5, 5, -1 ], [10, 4, -1 ], [15, 2, -1 ], [20, 1, 1 ], [25, 1, 2 ], [30, 0, 1 ], [35, 0, 1 ], [40, 0, 1 ], [45, 0, 1 ], [50, 0, 1 ], [55, 0, 0 ], [99, 0, 0 ] ]; value = im_lab_morph in.value (Vector [0, a_offset.value, b_offset.value] + grey_correction) L_offset L_scale ab_scale.value ab_scale.value; } } New_filename = New_pathname; Filename = Pathname "Pick a file"; New_colour = widget "Lab" [50,0,0] { widget default_colour value = class Colour colour_space value { _colourspaces = [ "XYZ", "Yxy", "Lab", "LCh", "UCS", "RGB", "sRGB" ]; colour_space_option = Option "Colour space" _colourspaces (index (equal default_colour) _colourspaces); colour_space = colour_space_option.labels? colour_space_option.value; Colour_edit colour_space value = widget colour_space value; } } ================================================ FILE: share/nip2/data/Makefile.am ================================================ nipdatadir = $(pkgdatadir)/data nipdata_DATA = \ rachel.con \ AdobeRGB1998.icc \ sRGB.icm \ macbeth_lab_d65.mat \ macbeth_lab_d50.mat \ vips-128.png \ nip2-icon.ico \ cmyk.icm \ stock-tool-ink-22.png \ stock-tool-path-22.png \ stock-tool-text-22.png \ stock-tool-smudge-22.png \ stock-tool-bucket-fill-22.png \ stock-tool-rect-select-22.png \ stock-tool-select-22.png \ stock-padlock-closed-22.png \ stock-alert-22.png \ nip-slider-16.png \ stock-tool-move-22.png \ stock-led-red-18.png \ stock-led-green-18.png \ stock-led-blue-18.png \ stock-led-cyan-18.png \ stock-led-yellow-18.png \ stock-led-off-18.png EXTRA_DIST = \ $(nipdata_DATA) \ examples install-exec-hook: rm -rf $(DESTDIR)$(nipdatadir)/examples $(mkinstalldirs) $(DESTDIR)$(nipdatadir)/examples cp -r ${top_srcdir}/share/nip2/data/examples/* $(DESTDIR)$(nipdatadir)/examples uninstall-hook: # make sure we have write permission for 'rm' chmod -R u+w ${DESTDIR}$(nipdatadir)/examples ${RM} -rf ${DESTDIR}$(nipdatadir)/examples ================================================ FILE: share/nip2/data/examples/1_point_mosaic/1pt_mosaic.ws ================================================ ================================================ FILE: share/nip2/data/examples/2_point_mosaic/2pts_mosaic.ws ================================================ ================================================ FILE: share/nip2/data/examples/businesscard/businesscard.ws ================================================ ================================================ FILE: share/nip2/data/examples/clone/clone.ws ================================================ ================================================ FILE: share/nip2/data/examples/framing/framing.ws ================================================ ================================================ FILE: share/nip2/data/examples/logo/logo2.ws ================================================ ================================================ FILE: share/nip2/data/examples/manual_balance/manual_balance.ws ================================================ ================================================ FILE: share/nip2/data/examples/overlays_and_blending/overlay_blend.ws ================================================ ================================================ FILE: share/nip2/data/examples/registering/registering.ws ================================================ ================================================ FILE: share/nip2/data/macbeth_lab_d50.mat ================================================ 3 24 38.82 14.52 14.86 65.97 16.84 16.71 50.25 -4.56 -21.94 43.69 -14.21 19.17 55.13 9.02 -24.66 70.1 -31.71 -0.78 64.17 34.77 62.36 40.28 10.55 -44.65 51.82 47.11 15.64 31 22.5 -21.72 72.3 -23.44 57.12 72.4 20.15 68.15 29.15 20.65 -55.37 54.78 -38.84 31.54 42.5 55.62 27.17 82.4 3.54 79.73 51.61 48.8 -14 50.03 -27.58 -29.2 94.27 -0.25 0.35 80.3 0.05 -0.38 65.41 0.09 -0.44 50.82 -0.37 -0.59 36.1 0.09 -0.49 23.8 0.54 -0.18 ================================================ FILE: share/nip2/data/macbeth_lab_d65.mat ================================================ 3 24 1 0 37.7758 13.122 13.2705 65.0651 13.6974 16.1079 51.125 -3.3471 -20.6654 42.9236 -12.4326 19.1425 56.0801 7.2918 -23.6413 70.8371 -30.663 2.5131 61.0209 34.1118 57.0494 42.4442 9.1559 -40.9195 50.2147 41.9414 12.8438 31.1403 21.5385 -23.1365 70.9067 -20.5043 57.9149 69.6498 20.8271 64.4978 32.4864 15.9125 -48.5415 54.1624 -34.1277 32.8876 40.4646 48.7723 24.0303 80.0696 4.3066 79.2876 50.847 42.549 -16.3102 52.5361 -28.7633 -23.2291 94.8303 -0.536 0.3512 80.3002 0.0736 -0.3646 65.3137 0.2192 -0.4743 50.7737 -0.2655 -0.546 36.005 0.2655 -0.5395 22.1402 0.6357 -0.219 ================================================ FILE: share/nip2/data/rachel.con ================================================ 5 5 21 0 1 -4 -4 -4 1 -4 6 8 6 -4 -4 8 9 8 -4 -4 6 8 6 -4 1 -4 -4 -4 1 ================================================ FILE: share/nip2/rc/Makefile.am ================================================ rcdir = $(pkgdatadir)/rc rc_DATA = ipgtkrc EXTRA_DIST = $(rc_DATA) ================================================ FILE: share/nip2/rc/ipgtkrc ================================================ # style for parent widgets style "parent_style" { bg[NORMAL] = "#887FA3" bg[PRELIGHT] = "#ADA7C8" bg[ACTIVE] = "#887FA3" bg[SELECTED] = "#887FA3" bg[INSENSITIVE] = "#625B81" } # style for child widgets style "child_style" { bg[NORMAL] = "#7590AE" bg[PRELIGHT] = "#9DB8D2" bg[ACTIVE] = "#7590AE" bg[SELECTED] = "#7590AE" bg[INSENSITIVE] = "#4B6983" } # style for selected widgets style "selected_style" { bg[NORMAL] = "#83A67F" bg[PRELIGHT] = "#C5D2C8" bg[ACTIVE] = "#83A67F" bg[SELECTED] = "#83A67F" bg[INSENSITIVE] = "#5D7555" } # style for unselected column headers style "column_style" { bg[NORMAL] = "#C5D2C8" bg[PRELIGHT] = "#C5D2C8" bg[ACTIVE] = "#C5D2C8" bg[SELECTED] = "#C5D2C8" bg[INSENSITIVE] = "#C5D2C8" } # style for widgets with errors in them style "error_style" { bg[NORMAL] = "#C1665A" bg[PRELIGHT] = "#E0B6AF" bg[ACTIVE] = "#C1665A" bg[SELECTED] = "#C1665A" bg[INSENSITIVE] = "#884631" } # style for dirty widgets (need recalculation) style "dirty_style" { bg[NORMAL] = "#E0C39E" bg[PRELIGHT] = "#EFE0CD" bg[ACTIVE] = "#E0C39E" bg[SELECTED] = "#E0C39E" bg[INSENSITIVE] = "#B39169" } # style for captions ... eg. the line of text under the image thumbnails style "caption_style" { bg[NORMAL] = "#EED680" } widget "*parent_widget*" style "parent_style" widget "*child_widget*" style "child_style" widget "*selected_widget*" style "selected_style" widget "*column_widget*" style "column_style" widget "*error_widget*" style "error_style" widget "*dirty_widget*" style "dirty_style" widget "*caption_widget*" style "caption_style" widget "*centre_widget*" style "child_style" widget "*shadow_widget*" style "column_style" # turn this on here ... no one will find this useful thing unless we turn it # on by default gtk-can-change-accels = 1 ================================================ FILE: share/nip2/start/Colour.def ================================================ Colour_new_item = class Menupullright (_ "_New") (_ "make a patch of colour") { Widget_colour_item = class Menuaction (_ "_Colour") (_ "make a patch of colour") { action = Colour_picker "Lab" [50,0,0]; } LAB_colour = class Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") { action = widget "Lab" [50, 0, 0]; // ab_slice size size = 512; // range of values ... +/- 128 for ab range = 256; // map xy in slice image to ab and back xy2ab x = x / (size / range) - 128; ab2xy a = (a + 128) * (size / range); widget space default_value = class Colour space _result { _vislevel = 3; [_L, _a, _b] = default_value; L = Scale "Lightness" 0 100 _L; ab_slice = Image (lab_slice size L.value); point = Mark ab_slice (ab2xy _a) (ab2xy _b); _result = [L.value, xy2ab point.left, xy2ab point.top]; Colour_edit colour_space value = widget colour_space value; } } CCT_colour = class Menuaction (_ "Colour from CCT") (_ "pick colour by CCT") { action = widget 6500; widget x = class _result { _vislevel = 3; T = Scale "CCT" 1800 25000 x; _result = colour_from_temp (to_real T); Colour_edit space value = widget (temp_from_colour (Colour space value)); } } } Colour_to_colour_item = class Menuaction (_ "Con_vert to Colour") (_ "convert anything to a colour") { action x = to_colour x; } #separator Colour_convert_item = class Menupullright (_ "_Colourspace") (_ "convert to various colour spaces") { spaces = Image_type.image_colour_spaces; conv dest x = class _result { _vislevel = 3; to = Option_enum (_ "Convert to") spaces (spaces.get_name dest); _result = map_unary (colour_transform_to to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") { action x = conv Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") { action x = conv Image_type.sRGB x; } scRGB_item = class Menuaction (_ "_scRGB") (_ "convert to scRGB colourspace") { action x = conv Image_type.scRGB x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "convert to GREY16 colourspace") { action x = conv Image_type.GREY16 x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "convert to RGB16 colourspace") { action x = conv Image_type.RGB16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") { action x = conv Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") { action x = conv Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") { action x = conv Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") { action x = conv Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") { action x = conv Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") { action x = conv Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "convert to UCS colourspace") { action x = conv Image_type.UCS x; } } /* mark objects as being in various colourspaces */ Colour_tag_item = class Menupullright (_ "_Tag As") (_ "tag object as being in various colour spaces") { spaces = Image_type.image_colour_spaces; tag dest x = class _result { _vislevel = 3; to = Option_enum (_ "Tag as") spaces (spaces.get_name dest); _result = map_unary (image_set_type to.value_thing) x; } Mono_item = class Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") { action x = tag Image_type.B_W x; } sRGB_item = class Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") { action x = tag Image_type.sRGB x; } scRGB_item = class Menuaction (_ "_scRGB") (_ "tag as being in scRGB colourspace") { action x = tag Image_type.scRGB x; } RGB16_item = class Menuaction (_ "_RGB16") (_ "tag as being in RGB16 colourspace") { action x = tag Image_type.RGB16 x; } GREY16_item = class Menuaction (_ "_GREY16") (_ "tag as being in GREY16 colourspace") { action x = tag Image_type.GREY16 x; } Lab_item = class Menuaction (_ "_Lab") (_ "tag as being in Lab colourspace (float Lab)") { action x = tag Image_type.LAB x; } LabQ_item = class Menuaction (_ "Lab_Q") (_ "tag as being in LabQ colourspace (32-bit Lab)") { action x = tag Image_type.LABQ x; } LabS_item = class Menuaction (_ "Lab_S") (_ "tag as being in LabS colourspace (48-bit Lab)") { action x = tag Image_type.LABS x; } LCh_item = class Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") { action x = tag Image_type.LCH x; } XYZ_item = class Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") { action x = tag Image_type.XYZ x; } Yxy_item = class Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") { action x = tag Image_type.YXY x; } UCS_item = class Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") { action x = tag Image_type.UCS x; } } Colour_temperature_item = class Menupullright (_ "Te_mperature") (_ "colour temperature conversions") { Whitepoint_item = class Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") { action x = class _result { _vislevel = 3; old_white = Option_enum (_ "Old whitepoint") Whitepoints "D65"; new_white = Option_enum (_ "New whitepoint") Whitepoints "D50"; _result = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im' * (new_white.value_thing / old_white.value_thing); im''' = colour_transform_to (get_type im) im''; } } } } D65_to_D50_item = class Menupullright (_ "D_65 to D50") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D65 to D50 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D652D50_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D652D50 im'; im''' = colour_transform_to (get_type im) im''; } } } } D50_to_D65_item = class Menupullright (_ "D_50 to D65") (_ "complex conversion") { XYZ_minimal_item = class Menuaction (_ "_Minimal") (_ "D50 to D65 using the minimal 3x3 matrix in XYZ") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = recomb D502D65_direct im'; im''' = colour_transform_to (get_type im) im''; } } } Bradford_item = class Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") { action x = map_unary process x { process im = im''' { im' = colour_transform_to Image_type.XYZ im; im'' = im_D502D65 im'; im''' = colour_transform_to (get_type im) im''; } } } } Lab_to_D50XYZ_item = class Menuaction (_ "_Lab to D50 XYZ") (_ "Lab to XYZ with a D50 whitepoint") { action x = map_unary (colour_unary im_D50Lab2XYZ) x; } D50XYZ_to_Lab_item = class Menuaction (_ "D50 _XYZ to Lab") (_ "XYZ to Lab with a D50 whitepoint") { action x = map_unary (colour_unary im_D50XYZ2Lab) x; } sep1 = Menuseparator; CCT_item = class Menuaction (_ "Calculate temperature") (_ "estimate CCT using the McCamy approximation") { action z = map_unary temp_from_colour z; } Colour_item = Colour_new_item.CCT_colour; } Colour_icc_item = class Menupullright (_ "_ICC") (_ "transform with ICC profiles") { print_profile = "$VIPSHOME/share/$PACKAGE/data/cmyk.icm"; monitor_profile = "$VIPSHOME/share/$PACKAGE/data/sRGB.icm"; guess_profile image = print_profile, has_type image && get_type image == Image_type.CMYK && has_bands image && get_bands image >= 4 = monitor_profile; render_intents = Option_enum (_ "Render intent") Render_intent.names (_ "Absolute"); Export_item = class Menuaction (_ "_Export") (_ "export from PCS to device space") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Output profile") print_profile; intent = render_intents; depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0; _result = map_unary process x { process image = icc_export [8, 16]?depth profile.value intent.value_thing lab { lab = colour_transform_to Image_type.LABQ image; } } } } Import_item = class Menuaction (_ "_Import") (_ "import from device space to PCS") { action x = class _result { _vislevel = 3; embedded = Toggle (_ "Use embedded profile if possible") false; profile = Pathname (_ "Default input profile") (guess_profile x); intent = render_intents; _result = map_unary process x { process image = icc_import_embedded intent.value_thing image, get_header_type "icc-profile-data" image != 0 && embedded = icc_import profile.value intent.value_thing image; } } } Transform_item = class Menuaction (_ "_Transform") (_ "transform between two device spaces") { action x = class _result { _vislevel = 3; in_profile = Pathname (_ "Input profile") (guess_profile x); out_profile = Pathname (_ "Output profile") print_profile; intent = render_intents; _result = map_unary process x { process image = icc_transform in_profile.value out_profile.value intent.value_thing image; } } } AC2RC_item = class Menuaction (_ "_Absolute to Relative") (_ "absolute to relative colorimetry using device profile") { action x = class _result { _vislevel = 3; profile = Pathname (_ "Pick a profile") (guess_profile x); _result = map_unary process x { process image = icc_ac2rc profile.value lab { lab = colour_transform_to Image_type.LAB image; } } } } } Colour_rad_item = class Menupullright (_ "_Radiance") (_ "convert to and from Radiance packed format") { Unpack_item = class Menuaction (_ "Unpack") (_ "unpack Radiance format to float") { action x = map_unary rad2float x; } Pack_item = class Menuaction (_ "Pack") (_ "pack 3-band float to Radiance format") { action x = map_unary float2rad x; } } #separator Colour_dE_item = class Menupullright (_ "_Difference") (_ "calculate colour difference") { /* Apply a converter to an object ... convert image or colour (since * we can guess the colour space we're converting from), don't convert * matrix or vector (since we can't tell ... assume it's in the right * space already). */ apply_cvt cvt x = cvt x, is_Image x || is_Colour x || is_image x = x; diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2); /* Converter to LAB. */ lab_cvt = colour_transform_to Image_type.LAB; /* Converter to UCS ... plain UCS is Ch form, so we go LAB again after * to make sure we get a rectangular coord system. */ ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @ colour_transform_to Image_type.UCS; CIEdE76_item = class Menuaction (_ "CIE dE _76") (_ "calculate CIE dE 1976 for two objects") { action a b = map_binary (diff lab_cvt) a b; } CIEdE00_item = class Menuaction (_ "CIE dE _00") (_ "calculate CIE dE 2000 for two objects") { action a b = map_binary (colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b; } UCS_item = class Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") { action a b = map_binary (diff ucs_cvt) a b; } } Colour_adjust_item = class Menupullright (_ "_Adjust") (_ "alter colours in various ways") { Recombination_item = class Menuaction (_ "_Recombination") (_ "recombine colour with an editable matrix") { action x = class _result { _vislevel = 3; matrix = Matrix_rec (identity_matrix (bands x)) { // try to guess a sensible value for the size of the // matrix bands x = x.bands, is_Image x || is_Colour x = x.width, is_Matrix x = bands x.value?0, is_Group x = x.bands, has_member "bands" x = 3; } _result = map_unary (recomb matrix) x; } } Cast_item = class Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") { action x = class _result { _vislevel = 3; gr = Scale "Green-red" (-20) 20 0; by = Scale "Blue-yellow" (-20) 20 0; _result = map_unary adjust_cast x { adjust_cast in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LAB in; in'' = in' + Vector [0, gr.value, by.value]; } } } } HSB_item = class Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") { action x = class _result { _vislevel = 3; h = Scale "Hue" 0 360 0; s = Scale "Saturation" 0.01 5 1; b = Scale "Brightness" 0.01 5 1; _result = map_unary adjust_hsb x { adjust_hsb in = colour_transform_to (get_type in) in'' { in' = colour_transform_to Image_type.LCH in; in'' = in' * Vector [b.value, s.value, 1] + Vector [0, 0, h.value]; } } } } } Colour_similar_item = class Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") { action x = class _result { _vislevel = 3; target_colour = Colour_picker "Lab" [50, 0, 0]; t = Scale "dE threshold" 0 100 10; _result = map_unary match x { match in = abs_vec (in' - target) < t { target = colour_transform_to Image_type.LAB target_colour; in' = colour_transform_to Image_type.LAB in; } } } } #separator Colour_chart_to_matrix_item = class Menuaction (_ "_Measure Colour Chart") (_ "measure average pixel values for a colour chart image") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; measure = Scale (_ "Measure area (%)") 1 100 50; // get a representative image from an arg get_image x = get_image x.value?0, is_Group x = x; _im = get_image x; sample = measure_draw (to_real pacross) (to_real pdown) (to_real measure) _im; _result = map_unary chart x { chart in = measure_sample (to_real pacross) (to_real pdown) (to_real measure) in; } } } Colour_matrix_to_chart_item = class Menuaction (_ "Make Synth_etic Colour Chart") (_ "make a colour chart image from a matrix of measurements") { action x = class _result { _vislevel = 3; pacross = Expression (_ "Patches across chart") 6; pdown = Expression (_ "Patches down chart") 4; pwidth = Expression (_ "Patch width in pixels") 50; pheight = Expression (_ "Patch height in pixels") 50; bwidth = Expression (_ "Border between patches") 0; _result = map_unary build_chart x { build_chart in = Image (imagearray_assemble (to_real bwidth) (to_real bwidth) patch_table) { // patch numbers for row starts rowstart = map (multiply (to_real pacross)) [0 .. to_real pdown - 1]; // assemble patches ... each one a pixel value patches = map (take (to_real pacross)) (map (converse drop in.value) rowstart); // make an n-band constant image from eg. [1,2,3] // we don't know the format .. use sRGB (well, why not?) patch v = image_new (to_real pwidth) (to_real pheight) (len v) Image_format.FLOAT Image_coding.NOCODING Image_type.sRGB (Vector v) 0 0; // make an image for each patch patch_table = map (map patch) patches; } } } } Colour_plot_ab_scatter_item = class Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") { action x = class _result { _vislevel = 3; bins = Expression (_ "Number of bins on each axis") 8; _result = map_unary plot_scatter x { plot_scatter in = Image (bg * (((90 / mx) * hist) ++ blk)) { lab = colour_transform_to Image_type.LAB in.value; ab = (unsigned char) ((lab?1 ++ lab?2) + 128); hist = hist_find_nD bins.expr ab; mx = max hist; bg = lab_slice bins.expr 1; blk = 1 + im_black (to_real bins) (to_real bins) 2; } } } } ================================================ FILE: share/nip2/start/Filter.def ================================================ Filter_conv_item = class Menupullright "_Convolution" "various spatial convolution filters" { /* Some useful masks. */ filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; filter_laplacian = Matrix_con 1 128 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; Blur_item = class Menuaction "_Blur" "3x3 blur of image" { action x = map_unary (conv filter_blur) x; } Sharpen_item = class Menuaction "_Sharpen" "3x3 sharpen of image" { action x = map_unary (conv filter_sharp) x; } Usharp_item = class Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { action x = class _result { _vislevel = 3; size = Option "Radius" [ "3 pixels", "5 pixels", "7 pixels", "9 pixels", "11 pixels", "51 pixels" ] 0; st = Scale "Smoothness threshold" 0 5 2; bm = Scale "Brighten by at most" 1 50 10; dm = Scale "Darken by at most" 1 50 20; fs = Scale "Sharpen flat areas by" 0 5 0.5; js = Scale "Sharpen jaggy areas by" 0 5 1; _result = map_unary process x { process in = Image in''' { in' = colour_transform_to Image_type.LABS in.value; in'' = sharpen [3, 5, 7, 9, 11, 51]?size st bm dm fs js in'; in''' = colour_transform_to (get_type in) in''; } } } } Emboss_item = class Menuaction "_Emboss" "1 pixel displace emboss" { action x = map_unary (conv filter_emboss) x; } Laplacian_item = class Menuaction "_Laplacian" "3x3 laplacian edge detect" { action x = map_unary (conv filter_laplacian) x; } Sobel_item = class Menuaction "So_bel" "3x3 Sobel edge detect" { action x = map_unary sobel x; } /* 3x3 line detect of image diagonals should be scaled down by root(2) I guess Kirk */ Linedet_item = class Menuaction "Li_ne Detect" "3x3 line detect" { action x = map_unary lindet x { lindet im = foldr1 max_pair images { masks = take 4 (iterate rot45 filter_lindet); images = map (converse conv im) masks; } } } Canny_item = class Menuaction "Canny" "Canny edge detector" { action x = class _result { _vislevel = 3; sigma = Scale "Sigma" 0.1 10 2; prec = Option "Precision" ["Int", "Float", "Approximate"] 1; _result = map_unary process x { process in = canny sigma prec.value in; } } } sep1 = Menuseparator; Custom_blur_item = class Menuaction "Custom B_lur / Sharpen" "blur or sharpen with tuneable parameters" { action x = class _result { _vislevel = 3; type = Option "Type" ["Blur", "Sharpen"] 0; r = Scale "Radius" 1 100 1; fac = Scale "Amount" 0 1 1; layers = Scale "Layers" 1 100 10; shape = Option "Mask shape" [ "Square", "Gaussian" ] 0; prec = Option "Precision" ["Int", "Float", "Approximate"] 0; _result = map_unary process x { process in = clip2fmt blur.format proc { mask = matrix_blur r.value, shape.value == 0 = matrix_gaussian_blur r.value; blur = [convsep, convsepf, aconvsep layers]?prec mask in; proc = in + fac * (in - blur), type == 1 = blur * fac + in * (1 - fac); } } } } Custom_conv_item = class Menuaction "Custom C_onvolution" "convolution filter with tuneable parameters" { action x = class _result { _vislevel = 3; matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; separable = Toggle "Seperable convolution" false, matrix.width == 1 || matrix.height == 1 = false; type = Option "Convolution type" ["Int", "Float"] 0; rotate = Option "Rotate" [ "Don't rotate", "4 x 45 degrees", "8 x 45 degrees", "2 x 90 degrees" ] 0; _result = map_unary process x { process in = in.Image in' { conv_fn = im_lindetect, !separable && type == 0 && rotate == 1 = im_compass, !separable && type == 0 && rotate == 2 = im_gradient, !separable && type == 0 && rotate == 3 = im_conv, !separable && type == 0 = im_convsep, separable && type == 0 = im_conv_f, !separable && type == 1 = im_convsep_f, separable && type == 1 = error "boink!"; in' = conv_fn in.value matrix; } } } } } Filter_rank_item = class Menupullright "_Rank" "various rank filters" { Median_item = class Menuaction "_Median" "3x3 median rank filter" { action x = map_unary (rank 3 3 4) x; } Image_rank_item = class Menuaction "_Image Rank" "pixelwise rank a list or group of images" { action x = class _result { _vislevel = 3; select = Expression "Rank" ((int) (guess_size / 2)) { guess_size = len x, is_list x = len x.value, is_Group x = 0; } // can't really iterate over groups ... since we allow a group // argument _result = rank_image select x; } } Custom_rank_item = class Menuaction "Custom _Rank" "rank filter with tuneable parameters" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 3; window_height = Expression "Window height" 3; select = Expression "Rank" ((int) ((to_real window_width * to_real window_height) / 2)); _result = map_unary process x { process in = rank window_width window_height select in; } } } } Filter_morphology_item = class Menupullright "_Morphology" "various morphological filters" { /* Some useful masks. */ mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; Threshold_item = Select_item.Threshold_item; sep1 = Menuseparator; Dilate_item = class Menupullright "_Dilate" "morphological dilate" { Dilate8_item = class Menuaction "_8-connected" "dilate with an 8-connected mask" { action x = map_unary (dilate mask8) x; } Dilate4_item = class Menuaction "_4-connected" "dilate with a 4-connected mask" { action x = map_unary (dilate mask4) x; } } Erode_item = class Menupullright "_Erode" "morphological erode" { Erode8_item = class Menuaction "_8-connected" "erode with an 8-connected mask" { action x = map_unary (erode mask8) x; } Erode4_item = class Menuaction "_4-connected" "erode with a 4-connected mask" { action x = map_unary (erode mask4) x; } } Custom_morph_item = class Menuaction "Custom _Morphology" "convolution morphological operator" { action x = class _result { _vislevel = 3; mask = mask4; type = Option "Operation" ["Erode", "Dilate"] 1; apply = Expression "Number of times to apply mask" 1; _result = map_unary morph x { morph image = Image value' { fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); value' = im_erode image.value fatmask, type.value == 0 = im_dilate image.value fatmask; } } } } sep2 = Menuseparator; Open_item = class Menuaction "_Open" "open with an 8-connected mask" { action x = map_unary (dilate mask8 @ erode mask8) x; } Close_item = class Menuaction "_Close" "close with an 8-connected mask" { action x = map_unary (erode mask8 @ dilate mask8) x; } Clean_item = class Menuaction "C_lean" "remove 8-connected isolated points" { action x = map_unary clean x { clean x = x ^ erode mask1 x; } } Thin_item = class Menuaction "_Thin" "thin once" { action x = map_unary thinall x { masks = take 8 (iterate rot45 thin); thin1 m x = x ^ erode m x; thinall x = foldr thin1 x masks; } } } Filter_fourier_item = class Menupullright "_Fourier" "various Fourier filters" { preview_size = 64; sense_option = Option "Sense" [ "Pass", "Reject" ] 0; // make a visualisation image make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask preview_size preview_size); // make the process function process fn in = (Image @ fn) (im_flt_image_freq in.value); New_ideal_item = class Menupullright "_Ideal" "various ideal Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f sense.value fc.value 0 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 6) fc.value rw.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject ideal Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_gaussian_item = class Menupullright "_Gaussian" "various Gaussian Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 4) fc.value ac.value 0 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 10) fc.value rw.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Gaussian Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; // call a freq func with our parameters _params f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } New_butterworth_item = class Menupullright "_Butterworth" "various Butterworth Fourier filters" { High_low_item = class Menuaction "_High or Low Pass" "highpass/lowpass Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 2) o.value fc.value ac.value 0 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" "ring pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 8) o.value fc.value rw.value ac.value 0; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } Band_item = class Menuaction "_Band Pass or Band Reject" "band pass/reject Butterworth Fourier filter" { action x = class _result { _vislevel = 3; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; o = Scale "Order" 1 10 2; // call a freq func with our parameters _params f = f (sense.value + 14) o.value fcx.value fcy.value r.value ac.value; visualize_mask = make_vis _params; _result = map_unary (process _params) x; } } } } Filter_enhance_item = class Menupullright "_Enhance" "various enhancement filters" { Falsecolour_item = class Menuaction "_False Colour" "false colour a mono image" { action x = class _result { _vislevel = 3; o = Scale "Offset" (-255) 255 0; clip = Toggle "Clip colour range" false; _result = map_unary process x { process im = falsecolour mono'' { mono = colour_transform_to Image_type.B_W im; mono' = mono + o; mono'' = (unsigned char) mono', clip = (unsigned char) (mono' & 0xff); } } } } Statistical_diff_item = class Menuaction "_Statistical Difference" "statistical difference of an image" { action x = class _result { _vislevel = 3; wsize = Expression "Window size" 11; tmean = Expression "Target mean" 128; mean_weight = Scale "Mean weight" 0 1 0.8; tdev = Expression "Target deviation" 50; dev_weight = Scale "Deviation weight" 0 1 0.8; border = Toggle "Output image matches input image in size" true; _result = map_unary process x { process in = Image in'' { in' = colour_transform_to Image_type.B_W in.value; fn = im_stdif, border = im_stdif_raw; in'' = fn in' mean_weight.value tmean.expr dev_weight.value tdev.expr wsize.expr wsize.expr; } } } } Hist_equal_item = class Menupullright "_Equalise Histogram" "equalise contrast" { Global_item = class Menuaction "_Global" "equalise contrast globally" { action x = map_unary hist_equalize x; } Local_item = class Menuaction "_Local" "equalise contrast within a roving window" { action x = class _result { _vislevel = 3; window_width = Expression "Window width" 20; window_height = Expression "Window height" 20; max_slope = Scale "Maxium slope" 0 10 0; _result = map_unary process x { process in = hist_equalize_local window_width window_height max_slope in; } } } } } Filter_correlate_item = class Menupullright "Spatial _Correlation" "calculate correlation surfaces" { Correlate_item = class Menuaction "_Correlate" "calculate correlation coefficient" { action a b = map_binary corr a b { corr a b = correlate a b, a.width <= b.width && a.height <= b.height = correlate b a; } } Correlate_fast_item = class Menuaction "_Simple Difference" "calculate sum of squares of differences" { action a b = map_binary corr a b { corr a b = correlate_fast a b, a.width <= b.width && a.height <= b.height = correlate_fast b a; } } } Filter_hough_item = class Menupullright "_Hough Transform" "transform to parameter space" { Line_item = class Menuaction "_Line" "find straight line Hough transform" { action a = class _result { _vislevel = 3; pspace_width = Expression "Parameter space width" 64; pspace_height = Expression "Parameter space height" 64; _result = map_unary line a { line a = hough_line (to_real pspace_width) (to_real pspace_height) a; } } } Circle_item = class Menuaction "_Circle" "find circle Hough transform" { action a = class _result { _vislevel = 3; scale = Expression "Scale down parameter space by" 10; min_radius = Expression "Minimum radius" 10; max_radius = Expression "Maximum radius" 30; _result = map_unary circle a { circle a = hough_circle (to_real scale) (to_real min_radius) (to_real max_radius) a; } } } } Filter_coordinate_item = class Menupullright "_Coordinate Transform" "various coordinate transforms" { // run a function which wants a complex arg on a non-complex two-band // image run_cmplx fn x = re x' ++ im x' { x' = fn (x?0, x?1); } Polar_item = class Menuaction "_Polar" "transform to polar coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_polar a { to_polar im = mapim interp.value map' im { // xy image, origin in the centre, scaled to fit image to // a circle xy = make_xy im.width im.height; xy' = xy - Vector [im.width / 2, im.height / 2]; scale = min [im.width, im.height] / im.width; xy'' = 2 * xy' / scale; // to polar, scale vertical axis to 360 degrees map = run_cmplx polar xy''; map' = map * Vector [1, im.height / 360]; } } } } Rectangular_item = class Menuaction "_Rectangular" "transform to rectangular coordinates" { action a = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary to_rect a { to_rect im = mapim interp.value map'' im { // xy image, vertical scaled to 360 degrees xy = make_xy im.width im.height; xy' = xy * Vector [1, 360 / im.height]; // to rect, scale to image rect map = run_cmplx rectangular xy'; scale = min [im.width, im.height] / im.width; map' = map * scale / 2; map'' = map' + Vector [im.width / 2, im.height / 2]; } } } } } #separator Filter_tilt_item = class Menupullright "Ti_lt Brightness" "tilt brightness" { Left_right_item = class Menuaction "_Left to Right" "linear left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height; scale = (ramp - 0.5) * tilt + 1; } } } } Top_bottom_item = class Menuaction "_Top to Bottom" "linear top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width); scale = (ramp - 0.5) * tilt + 1; } } } } sep1 = Menuseparator; Left_right_cos_item = class Menuaction "Cosine Left-_right" "cosine left-right brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Left-right tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_lr x { tilt_lr image = image * scale { ramp = im_fgrey image.width image.height - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } Top_bottom_cos_item = class Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Top-bottom tilt" (-1) 1 0; shift = Scale "Shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { ramp = rot90 (im_fgrey image.height image.width) - 0.5 - shift.value; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } sep2 = Menuseparator; Circular_item = class Menuaction "_Circular" "circular brighten" { action x = class _result { _vislevel = 3; tilt = Scale "Tilt" (-1) 1 0; hshift = Scale "Horizontal shift by" (-1) 1 0; vshift = Scale "Vertical shift by" (-1) 1 0; _result = map_unary tilt_tb x { tilt_tb image = image * scale { hramp = im_fgrey image.width image.height - 0.5 - hshift.value; vramp = rot90 (im_fgrey image.height image.width) - 0.5 - vshift.value; ramp = (hramp ** 2 + vramp ** 2) ** 0.5; scale = 0.5 * tilt.value * cos (ramp * 180) + 1; } } } } } Filter_blend_item = class Menupullright "_Blend" "blend objects together" { Scale_blend_item = class Menuaction "_Scale" "blend two objects together with a scale" { action a b = class _result { _vislevel = 3; p = Scale "Blend position" 0 1 0.5; _result = map_binary process a b { process im1 im2 = im1 * (1 - p.value) + im2 * p.value; } } } Image_blend_item = class Menuaction "_Image" "use an image to blend two objects" { action a b c = class _result { _vislevel = 3; i = Toggle "Invert mask" false; _result = map_trinary process a b c { process a b c = blend condition in1 in2, !i = blend (invert condition) in1 in2 { compare a b // prefer image as the condition = false, !has_image a && has_image b // prefer mono images as the condition = false, has_bands a && has_bands b && get_bands a > 1 && get_bands b == 1 // prefer uchar as the condition = false, has_format a && has_format b && get_format a > Image_format.UCHAR && get_format b == Image_format.UCHAR = true; [condition, in1, in2] = sortc compare [a, b, c]; } } } } Line_blend_item = class Menuaction "_Along Line" "blend between image a and image b along a line" { action a b = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Left to Right", "Top to Bottom" ] 0; blend_position = Scale "Blend position" 0 1 0.5; blend_width = Scale "Blend width" 0 1 0.05; _result = map_binary process a b { process a b = blend (Image condition) b a { output_width = max_pair a.width b.width; output_height = max_pair a.height b.height; range = output_width, orientation == 0 = output_height; blend_position' = floor (range * blend_position.value); blend_width' = 1, blend_width.value == 0 = floor (range * blend_width.value); start = blend_position' - blend_width' / 2; background = (make_xy output_width output_height) >= blend_position'; ramp = im_grey blend_width' output_height, orientation == 0 = rot90 (im_grey blend_width' output_width); condition = insert_noexpand start 0 ramp background?0, orientation == 0 = insert_noexpand 0 start ramp background?1; } } } } Blend_alpha_item = class Menuaction "Blend _Alpha" "blend images with optional alpha channels" { // usage: layerit foreground background // input images must be either 1 or 3 bands, optionally + 1 band // which is used as the alpha channel // rich lott scale_mask im opacity = (unsigned char) (to_real opacity / 255 * im); // to mono intensity = colour_transform_to Image_type.B_W; // All the blend functions // I am grateful to this page // http://www.pegtop.net/delphi/blendmodes/ // for most of the formulae. blend_normal mask opacity fg bg = blend (scale_mask mask opacity) fg bg; blend_iflighter mask opacity fg bg = blend (if fg' > bg' then mask' else 0) fg bg { fg' = intensity fg; bg' = intensity bg; mask' = scale_mask mask opacity ; } blend_ifdarker mask opacity fg bg = blend (if fg' < bg' then mask' else 0) fg bg { fg' = intensity fg ; bg' = intensity bg ; mask' = scale_mask mask opacity ; } blend_multiply mask opacity fg bg = blend (scale_mask mask opacity) fg' bg { fg' = fg / 255 * bg; } blend_add mask opacity fg bg = blend mask fg' bg { fg' = opacity / 255 * fg + bg; } blend_subtract mask opacity fg bg = blend mask fg' bg { fg' = bg - opacity / 255 * fg; } blend_screen mask opacity fg bg = blend mask fg' bg { fg' = 255 - (255 - bg) * (255 - (opacity / 255 * fg)) / 255; } blend_burn mask opacity fg bg = blend mask fg'' bg { // fades to white which has no effect. fg' = (255 - opacity) + opacity * fg / 255; fg'' = 255 - 255 * (255 - bg) / fg'; } blend_softlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = (2 * bg * fg + bg * bg * (1 - 2 * fg / 255)) / 255; } blend_hardlight mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 2 / 255 * fg * bg, bg < 129 = 255 - 2 * (255 - bg) * (255 - fg) / 255; } blend_lighten mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg < fg then fg else bg; } blend_darken mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = if bg > fg then fg else bg; } blend_dodge mask opacity fg bg = blend mask fg'' bg { // one added to avoid divide by zero fg' = 1 + 255 - (opacity / 255 * fg); fg'' = bg * 255 / fg'; } blend_reflect mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = bg * bg / (255 - fg); } blend_freeze mask opacity fg bg = blend mask' fg' bg { mask' = scale_mask mask opacity; fg' = 255 - (255 - bg) * (255 - bg) / (1 + fg); } blend_or mask opacity fg bg = bg | (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } blend_and mask opacity fg bg = bg & (unsigned char) fg' { mask' = scale_mask mask opacity; fg' = fg * mask' / 255; } // blend types NORMAL = 0; IFLIGHTER = 1; IFDARKER = 2; MULTIPLY = 3; ADD = 4; SUBTRACT = 5; SCREEN = 6; BURN = 7; DODGE = 8; HARDLIGHT = 9; SOFTLIGHT = 10; LIGHTEN = 11; DARKEN = 12; REFLECT = 13; FREEZE = 14; OR = 15; AND = 16; // names we show the user for blend types names = Enum [ _ "Normal" => NORMAL, _ "If Lighter" => IFLIGHTER, _ "If Darker" => IFDARKER, _ "Multiply" => MULTIPLY, _ "Add" => ADD, _ "Subtract" => SUBTRACT, _ "Screen" => SCREEN, _ "Burn" => BURN, _ "Soft Light" => SOFTLIGHT, _ "Hard Light" => HARDLIGHT, _ "Lighten" => LIGHTEN, _ "Darken" => DARKEN, _ "Dodge" => DODGE, _ "Reflect" => REFLECT, _ "Freeze" => FREEZE, _ "Bitwise OR" => OR, _ "Bitwise AND" => AND ]; // functions we call for each blend type actions = Table [ [NORMAL, blend_normal], [IFLIGHTER, blend_iflighter], [IFDARKER, blend_ifdarker], [MULTIPLY, blend_multiply], [ADD, blend_add], [SUBTRACT, blend_subtract], [SCREEN, blend_screen], [BURN, blend_burn], [SOFTLIGHT, blend_softlight], [HARDLIGHT, blend_hardlight], [LIGHTEN, blend_lighten], [DARKEN, blend_darken], [DODGE, blend_dodge], [REFLECT, blend_reflect], [FREEZE, blend_freeze], [OR, blend_or], [AND, blend_and] ]; // make sure im has an alpha channel (set opaque if it hasn't) put_alpha im = im, im.bands == 4 || im.bands == 2 = im ++ 255; // make sure im has no alpha channel lose_alpha im = extract_bands 0 3 im, im.bands == 4 = im?0, im.bands == 2 = im; // does im have al alpha channel? has_alpha im = im.bands == 2 || im.bands == 4; // get the alpha (set opaque if no alpha) get_alpha img = img'?3, img.bands == 4 = img'?1 { img' = put_alpha img; } // add an alpha ... cast the alpha image to match the main image append_alpha im alpha = im ++ clip2fmt im.format alpha; // makes fg the same size as bg, displaced with u, v pixel offset moveit fg bg u v = insert_noexpand u v fg bg' { bg' = image_new bg.width bg.height fg.bands fg.format fg.coding fg.type 0 0 0; } action bg fg = class _value { _vislevel = 3; method = Option_enum "Blend mode" names "Normal"; opacity = Scale "Opacity" 0 255 255; hmove = Scale "Horizontal move by" (-bg.width) (bg.width) 0; vmove = Scale "Vertical move by" (-bg.height) (bg.height) 0; _value = append_alpha blended merged_alpha, has_alpha bg = blended { // displace and resize fg (need to displace alpha too) fg' = moveit (put_alpha fg) bg hmove vmove; // transform to sRGB fg'' = colour_transform_to Image_type.sRGB (lose_alpha fg'); bg' = colour_transform_to Image_type.sRGB (lose_alpha bg); // alphas merged merged_alpha = get_alpha bg | get_alpha fg'; // blend together blended = (actions.lookup 0 1 method.value_thing) (get_alpha fg') opacity.value fg'' bg'; } } } } Filter_overlay_header_item = class Menuaction "_Overlay" "make a colour overlay of two monochrome images" { action a b = class _result { _vislevel = 3; colour = Option "Colour overlay as" [ _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = map_binary overlay a b { overlay a b = image_set_type Image_type.sRGB [(a' ++ b' ++ 0), (a' ++ 0 ++ b'), (b' ++ a' ++ 0), (b' ++ 0 ++ a'), (0 ++ a' ++ b'), (0 ++ b' ++ a')]?colour { a' = colour_transform_to Image_type.B_W a; b' = colour_transform_to Image_type.B_W b; } } } } Filter_colourize_item = class Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { action a b = class _result { _vislevel = 3; tint = Scale "Tint" 0 1 0.6; _result = map_binary tintit a b { tintit a b = colour_transform_to (get_type colour) colourized' { // get the mono thing first [mono, colour] = sortc (const (is_colour_type @ get_type)) [a, b]; colour' = tint * colour_transform_to Image_type.LAB colour; mono' = colour_transform_to Image_type.B_W mono; colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; colourized' = image_set_type Image_type.LAB colourized; } } } } Filter_browse_multiband_item = class Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { Bandwise_item = class Menuaction "B_andwise" "browse through the bands of a multiband image" { action image = class _result { _vislevel = 3; band = Scale "Band" 0 (image.bands - 1) 0; display = Option "Display as" [ _ "Grey", _ "Green over Red", _ "Blue over Red", _ "Red over Green", _ "Red over Blue", _ "Blue over Green", _ "Green over Blue" ] 0; _result = output { down = (int) band.value; up = down + 1; remainder = band.value - down; fade x a = Vector [0], x == 0 = a * x; a = fade remainder image?up; b = fade (1 - remainder) image?down; output = [ a + b, a ++ b ++ 0, a ++ 0 ++ b, b ++ a ++ 0, b ++ 0 ++ a, 0 ++ a ++ b, 0 ++ b ++ a ] ? display; } } } Bitwise_item = class Menuaction "Bi_twise" "browse through the bits of an image" { action x = class _result { _vislevel = 3; bit = Islider "Bit" 0 (nbits - 1) (nbits - 1) { nbits = x.bits, is_Image x = 8; Islider c f t v = class scope.Scale c f t ((int) v) { Scale = Islider; } } _result = map_unary process x { process im = (im & (0x1 << bit.value)) != 0; } } } } #separator Filter_negative_item = class Menuaction "Photographic _Negative" "swap black and white" { action x = map_unary invert x { invert in = clip2fmt in.format (colour_transform_to (get_type in) rgb') { rgb = colour_transform_to Image_type.sRGB in; rgb' = 255 - rgb; } } } Filter_solarize_item = class Menuaction "_Solarise" "invert colours above a threshold" { action x = class _result { _vislevel = 3; kink = Scale "Kink" 0 1 0.5; _result = map_unary process x { process image = hist_map tab'''' image { // max pixel value for this format mx = Image_format.maxval image.format; // make a LUT ... just 8 and 16 bit tab = im_identity_ushort image.bands mx, image.format == Image_format.USHORT = im_identity image.bands; tab' = Image tab; // make basic ^ shape tab'' = tab' * (1 / kink), tab' < mx * kink = (mx - tab') / (1 - kink); tab''' = clip2fmt image.format tab''; // smooth a bit mask = matrix_blur (tab'''.width / 8); tab'''' = convsep mask tab'''; } } } } Filter_diffuse_glow_item = class Menuaction "_Diffuse Glow" "add a halo to highlights" { action x = class _result { _vislevel = 3; r = Scale "Radius" 0 50 5; highlights = Scale "Highlights" 0 100 95; glow = Scale "Glow" 0 1 0.5; colour = Colour_new_item.Widget_colour_item.action; _result = map_unary process x { process image = image' { mono = (unsigned char) (colour_transform_to Image_type.B_W image); thresh = hist_thresh (highlights.value / 100) mono; mask = mono > thresh; blur = convsep (matrix_gaussian_blur r.value) mask; colour' = colour_transform_to image.type colour; image' = image + colour' * glow * (blur / 255); } } } } Filter_drop_shadow_item = class Menuaction "Drop S_hadow" "add a drop shadow to an image" { action x = class _result { _vislevel = 3; sx = Scale "Horizontal shadow" (-50) 50 5; sy = Scale "Vertical shadow" (-50) 50 5; ss = Scale "Shadow softness" 0 20 5; bg_colour = Expression "Background colour" 255; sd_colour = Expression "Shadow colour" 128; alpha = Toggle "Shadow in alpha channel" false; transparent = Toggle "Zero pixels are transparent" false; _result = map_unary shadow x { shadow image = Image final { blur_size = ss.value * 2 + 1; // matrix we blur with to soften shadows blur_matrix = matrix_gaussian_blur blur_size; matrix_size = blur_matrix.width; matrix_radius = (int) (matrix_size / 2) + 1; // position and size of shadow image in input cods // before and after fuzzing shadow_rect = Rect sx.value sy.value image.width image.height; fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; // size and pos of final image, in input cods final_rect = image.rect.union fuzzy_shadow_rect; // hard part of shadow in output cods shadow_rect' = Rect (shadow_rect.left - final_rect.left) (shadow_rect.top - final_rect.top) shadow_rect.width shadow_rect.height; // make the shadow mask ... true for parts which cast // a shadow mask = (foldr1 bitwise_and @ bandsplit) (image.value != 0), transparent = image_new image.width image.height 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W 255 0 0; mask' = embed 0 shadow_rect'.left shadow_rect'.top final_rect.width final_rect.height mask; mask'' = convsep blur_matrix mask'; // use mask to fade between bg and shadow colour mk_background colour = image_new final_rect.width final_rect.height image.bands image.format image.coding image.type colour 0 0; bg_image = mk_background bg_colour.expr; shadow_image = mk_background sd_colour.expr; bg = blend mask'' shadow_image bg_image; // make a full size mask fg_mask = embed 0 (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) final_rect.width final_rect.height mask; // wrap up the input image ... put the shadow colour // around it, so if we are outputting a separate // alpha the shadow colour will be set correctly fg = insert (image.rect.left - final_rect.left) (image.rect.top - final_rect.top) image.value shadow_image; final // make a separate alpha = fg ++ mask'', alpha // paste image over shadow = if fg_mask then fg else bg; } } } } Filter_paint_text_item = class Menuaction "_Paint Text" "paint text into an image" { action x = paint_position, is_Group x = paint_area { paint_area = class _result { _check_args = [ [x, "x", check_Image] ]; _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; align = Option "Alignment" ["Left", "Centre", "Right"] 0; dpi = Expression "DPI" 300; colour = Expression "Text colour" 255; place = Region x (x.width / 4) (x.height / 4) (x.width / 2) (x.height / 2); _result = insert_noexpand place.left place.top (blend txt' fg place) x { fg = image_new place.width place.height x.bands x.format x.coding x.type colour.expr 0 0; txt = Image (im_text text.value font.value place.width align.value (to_real dpi)); bg = im_black place.width place.height 1; txt' = insert_noexpand 0 0 txt bg; } } paint_position = class _result { _vislevel = 3; text = Pattern_images_item.Text_item.action; colour = Expression "Text colour" 255; position = Option "Position" [ _ "North-west", _ "North", _ "North-east", _ "West", _ "Centre", _ "East", _ "South-west", _ "South", _ "South-east", _ "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary paint x { paint image = insert_noexpand x' y' place' image { xr = image.width - text.width; yr = image.height - text.height; x = left.expr, position == 9 = [0, xr / 2, xr]?(position % 3); y = top.expr, position == 9 = [0, yr / 2, yr]?(position / 3); x' = range 0 x (image.width - 1); y' = range 0 y (image.height - 1); w' = range 1 text.width (image.width - x'); h' = range 1 text.height (image.height - y'); place = extract_area x' y' w' h' image; text' = insert_noexpand 0 0 text (im_black w' h' 1); fg = image_new w' h' image.bands image.format image.coding image.type colour.expr 0 0; place' = blend text' fg place; } } } } } Autotrace_item = class Menuaction "_Trace" "convert a bitmap to an SVG file" { action x = class _result { _vislevel = 3; despeckle = Scale "Despeckle level" 1 20 1; line = Scale "Line threshold" 1 20 1; center = Toggle "Trace centreline" false; scale = Scale "SVG scale" 0.1 10 1; command = "autotrace %s " ++ join_sep " " [ofmt, ofile, desp, lint, cent] { prog = search_for_error "autotrace"; ofmt = "-output-format svg"; ofile = "-output-file %s"; desp = "-despeckle-level " ++ print despeckle.value; lint = "-line-threshold " ++ print line.value; cent = if center then "-centerline " else ""; } _result = Image output { [output] = vips_call "system" [command] [$in => [x.value], $in_format => "%s.ppm", $out => true, $out_format => "%s.svg[scale=" ++ print scale.value ++ "]" ]; } } } ================================================ FILE: share/nip2/start/Histogram.def ================================================ Hist_new_item = class Menupullright "_New" "new histogram" { Hist_item = class Menuaction "_Identity" "make an identity histogram" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; _result = Plot [] ([im_identity 1, im_identity_ushort 1 65536]?d); } } Hist_new_from_matrix = Matrix_buildlut_item; Hist_from_image_item = class Menuaction "Ta_g Image As Histogram" "set image Type to Histogram" { action x = hist_tag x; } Tone_item = class Menuaction "_Tone Curve" "make a new tone mapping curve" { action = class _result { _vislevel = 3; d = Option "Depth" ["8 bit", "16 bit"] 0; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; _result = tone_build fmt b w sp mp hp sa ma ha { fmt = [Image_format.UCHAR, Image_format.USHORT]?d; } } } } Hist_convert_to_hist_item = class Menuaction "Con_vert to Histogram" "convert anything to a histogram" { action x = hist_tag (to_image x); } Hist_find_item = class Menupullright "_Find" "find a histogram" { Oned_item = class Menuaction "_One Dimension" "for a n-band image, make an n-band 1D histogram" { action x = map_unary hist_find x; } Nd_item = class Menuaction "_Many Dimensions" "for a n-band image, make an n-dimensional histogram" { action x = class _result { _vislevel = 3; // default to something small-ish bins = Expression "Number of bins in each dimension" 8; _result = map_unary process x { process in = hist_find_nD bins in; } } } Indexed_item = class Menuaction "_Indexed" "use a 1-band index image to pick bins for an n-band image" { action x y = class _result { _vislevel = 3; combine = Combine_picker Combine_type.SUM; _result = map_binary map x y { map a b = hist_find_indexed combine.value index im { [im, index] = sortc (const is_index) [a, b]; is_index x = has_image x && b == 1 && (f == Image_format.UCHAR || f == Image_format.USHORT) { im = get_image x; b = get_bands x; f = get_format x; } } } } } } Hist_map_item = class Menuaction "_Map" "map an image through a histogram" { action x y = map_binary map x y { map a b = hist_map hist im { [im, hist] = sortc (const is_hist) [a, b]; } } } Hist_eq_item = Filter_enhance_item.Hist_equal_item; #separator Hist_cum_item = class Menuaction "_Integrate" "form cumulative histogram" { action x = map_unary hist_cum x; } Hist_diff_item = class Menuaction "_Differentiate" "find point-to-point differences (inverse of Integrate)" { action x = map_unary hist_diff x; } Hist_norm_item = class Menuaction "N_ormalise" "normalise a histogram" { action x = map_unary hist_norm x; } Hist_inv_item = class Menuaction "In_vert" "invert a histogram" { action x = map_unary hist_inv x; } Hist_match_item = class Menuaction "Ma_tch" "find LUT which will match first histogram to second" { action in ref = map_binary hist_match in ref; } Hist_zerox_item = class Menuaction "_Zero Crossings" "find zero crossings" { action x = class _result { _vislevel = 3; edge = Option "Direction" [ "Positive-going", "Negative-going" ] 0; _result = map_unary (zerox (if edge == 0 then -1 else 1)) x; } } Hist_entropy_item = class Menuaction "Entropy" "calculate histogram entropy" { action x = hist_entropy x; } #separator Hist_profile_item = class Menuaction "Find _Profile" "search from image edges for non-zero pixels" { action x = class _result { _vislevel = 3; edge = Option "Search from" [ "Top edge down", "Left edge to right", "Bottom edge up", "Right edge to left" ] 2; _result = map_unary profile x { profile image = (Plot_histogram @ hist_tag) [ profilemb 0 image.value, profilemb 1 image.value, profilemb 0 (fliptb image.value), profilemb 1 (fliplr image.value) ]?edge; // im_profile only does 1 band images :-( profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit; } } } Hist_project_item = class Menuaction "Find Pro_jections" "find horizontal and vertical projections" { action x = class { _vislevel = 2; _result = map_unary project x; // extract the result ... could be a group extr n = Plot_histogram _result?n, is_list _result = Group (map (Plot_histogram @ converse subscript n) _result.value); horizontal = extr 0; vertical = extr 1; centre = (gravity horizontal, gravity vertical); } } #separator Hist_graph_item = class Menuaction "P_lot Slice" "plot a slice along a guide or arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary graph x { graph arrow = hist_tag area' { area = extract_arrow displace.value vdisplace.value width.value arrow; // squish vertically to get an average area' = resize Kernel_linear 1 (1 / width.value) area; } } } } Extract_arrow_item = class Menuaction "Extract _Arrow" "extract the area around an arrow" { action x = class _value { _vislevel = 3; width = Scale "Width" 1 40 1; displace = Scale "Horizontal displace" (-50) 50 0; vdisplace = Scale "Vertical displace" (-50) 50 0; _value = map_unary (extract_arrow displace.value vdisplace.value width.value) x; } } Hist_plot_item = class Menuaction "Plot _Object" "plot an object as a bar, point or line graph" { action x = class _result { _vislevel = 3; caption = Expression "Chart caption" "none"; format = Option_enum "Format" Plot_format.names "YYYY"; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; xcaption = Expression "X axis caption" "none"; ycaption = Expression "Y axis caption" "none"; series_captions = Expression "Series captions" ["Band 0"]; _result = Plot options (image x) { options = [$style => style.value, $format => format.value] ++ range ++ captions; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; captions = concat (map test caption_options) ++ [$series_captions => series_captions.expr] { caption_options = [ $caption => caption.expr, $xcaption => xcaption.expr, $ycaption => ycaption.expr ]; test x = [], value == "none" = [option_name => value] { [option_name, value] = x; } } image x = image (extract_arrow 0 0 1 x), is_Arrow x = get_image x, has_image x = x2b im, b == 1 = im { im = get_image (to_image x); w = get_width im; h = get_height im; b = get_bands im; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { extract_col x = extract_area x 0 1 h im; } } } } } ================================================ FILE: share/nip2/start/Image.def ================================================ Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process = [ // we can't use id here since we want to "declass" // the members of x ... consider if x is a crop class, // for example, we don't want to inherit from crop, we // want to make a new image class rot180 @ rot180, rot90, rot180, rot270 ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { process image = resize kernel h v image, aspect = resize kernel fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; kernel = Kernel_picker Kernel_type.LINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize kernel fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_map_item = class Menuaction "_Map" "map an image through a 2D transform image" { action a b = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_binary trans a b { trans a b = mapim interp.value in index { // get the index image first [index, in] = sortc (const is_twocomponent) [a, b]; // is a two-component image, ie. one band complex, or // two-band non-complex is_twocomponent x = is_nonc x || is_c x; is_nonc x = has_bands x && get_bands x == 2 && has_format x && !is_complex_format (get_format x); is_c x = has_bands x && get_bands x == 1 && has_format x && is_complex_format (get_format x); is_complex_format f = f == Image_format.COMPLEX || f == Image_format.DPCOMPLEX; } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1a = Menuseparator; Bandand_item = class Menuaction "Bitwise Band AND" "bitwise AND of image bands" { action x = bandand x; } Bandor_item = class Menuaction "Bitwise Band OR" "bitwise OR of image bands" { action x = bandor x; } sep2 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_alpha_item = class Menupullright "_Alpha" "manipulate image alpha" { Add_item = class Menuaction "_Add" "add alpha" { action x = class _result { _vislevel = 3; opacity = Expression "Opacity (255 == solid)" 255; _result = x ++ to_real opacity; } } Flatten_item = class Menuaction "_Flatten" "flatten alpha out of image" { action x = class _result { _vislevel = 3; bg = Expression "Background" 0; _result = map_unary (flattenimage bg) x; } } Extract_item = class Menuaction "_Extract" "extract alpha" { action x = map_unary exb x { exb x = extract_bands (x.bands - 1) 1 x; } } Drop_item = class Menuaction "_Drop" "drop alpha" { action x = map_unary exb x { exb x = extract_bands 0 (x.bands - 1) x; } } sep1 = Menuseparator; Premultiply_item = class Menuaction "_Premultiply" "premultiply alpha" { action x = premultiply x; } Unpremultiply_item = class Menuaction "_Unpremultiply" "unpremultiply alpha" { action x = unpremultiply x; } sep2 = Menuseparator; Composite2_item = class Menuaction "_Composite two" "composite a pair of images" { action x y = class _result { _vislevel = 3; blend = Option_enum (_ "Blend mode") modes "over" { modes = Blend_type.types; } compositing_space = Option_enum (_ "Compositing space") spaces "sRGB" { spaces = Image_type.image_colour_spaces; } premultiplied = Toggle (_ "Premultiplied") false; _result = Image output { [output] = vips_call "composite" [[y.value, x.value], blend.value] [$compositing_space => compositing_space.value_thing, $premultiplied => premultiplied.value ]; } } } Composite3_item = class Menuaction "_Composite three" "composite three images" { action x y z = class _result { _vislevel = 3; blend1 = Option_enum (_ "Blend mode") modes "over" { modes = Blend_type.types; } blend2 = Option_enum (_ "Blend mode") modes "over" { modes = Blend_type.types; } compositing_space = Option_enum (_ "Compositing space") spaces "sRGB" { spaces = Image_type.image_colour_spaces; } premultiplied = Toggle (_ "Premultiplied") false; _result = Image output { [output] = vips_call "composite" [[z.value, y.value, x.value], [blend1.value, blend2.value]] [$compositing_space => compositing_space.value_thing, $premultiplied => premultiplied.value ]; } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = crop x [l, t, w, h] { fields = [ [has_left, get_left, 0], [has_top, get_top, 0], [has_width, get_width, 100], [has_height, get_height, 100] ]; [l, t, w, h] = map get_default fields { get_default line = get x, has x = default { [has, get, default] = line; } } } crop x geo = class _result { _vislevel = 3; l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Trim_item = class Menuaction "_Trim" "crop away edges" { action x = class _result { _vislevel = 3; thresh = Scale "threshold" 0 100 10; background = Expression "Background" default_background { default_background = map mean (bandsplit (extract_area 0 0 1 1 x)); } _result = Region x l t w h { [l, t, w, h] = vips_call "find_trim" [x.value] [ $threshold => thresh.value, $background => background.expr ]; } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; i = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1 y1 x2 y2 i.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; rx = Expression "Left" 50; ry = Expression "Top" 50; rw = Expression "Width" 100; rh = Expression "Height" 100; f = Toggle "Fill" true; t = Scale "Line thickness" 1 50 3; i = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect_width rx ry rw rh f t i.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; f = Toggle "Fill" true; i = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx cy r f i.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; sx = Expression "Start x" 0; sy = Expression "Start y" 0; e = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can i = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); im = get_image x; } _result = map_unary flood x { flood im = draw_flood sx sy i.expr im, e == 0 = draw_flood_blob sx sy i.expr im; } } } Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { action x = class _result { _vislevel = 3; px = Expression "Left" 50; py = Expression "Top" 50; wid = Expression "Width" 100; thick = Scale "Line thickness" 1 50 3; text = String "Dimension text" "50μm"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; pos = Option "Position Text" ["Above", "Below"] 1; vp = Option "Dimension by" [ "Inner Vertical Edge", "Centre of Vertical", "Outer Vertical Edge" ] 1; dpi = Expression "DPI" 100; ink = Colour "Lab" [50,0,0]; _result = map_unary process x { process im = blend (Image scale) ink' im { // make an ink compatible with the image ink' = colour_transform_to (get_type im) ink; x = to_real px; y = to_real py; w = to_real wid; d = to_real dpi; t = floor thick; bg = image_new (get_width im) (get_height im) (get_bands im) (get_format im) (get_coding im) (get_type im) 0 0 0; draw_block x y w t im = draw_rect_width x y w t true 1 [255] im; label = im_text text.value font.value w 1 d; lw = get_width label; lh = get_height label; ly = [y - lh - t, y + 2 * t]?pos; vx = [ [x - t, x + w], [x - t / 2, x + w - t / 2], [x, x + w - t] ]?vp; scale = (draw_block x y w t @ draw_block vx?0 (y - 2 * t) t (t * 5) @ draw_block vx?1 (y - 2 * t) t (t * 5) @ insert_noexpand (x + w / 2 - lw / 2) ly label) bg; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise Join" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; snake = Toggle "Reverse the order of every other row" false; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _l' = map2 reverse_if_odd [0..] _l, snake = _l { reverse_if_odd n x = reverse x, n % 2 == 1 = x; } _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l')); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Noise_item = class Menupullright "_Noise" "various noise generators" { Gaussian_item = class Menuaction "_Gaussian" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (gaussnoise nwidth nheight mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal noise image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Perlin_item = class Menuaction "_Perlin" "Perlin noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; cell_size = Expression "Cell size (pixels)" 8; eight = Toggle "Eight bit output" true; _result = 128 * im + 128, eight = im { im = perlin cell_size nwidth nheight; } } } Worley_item = class Menuaction "_Worley" "Worley noise image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 512; nheight = Expression "Image height (pixels)" 512; cell_size = Expression "Cell size (pixels)" 256; _result = worley cell_size nwidth nheight; } } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; fontfile = Pathname "Font file" ""; width = Expression "Text width" 0; height = Expression "Text height" 0; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; fit = Toggle "Fit text to box" false; spacing = Expression "Line spacing" 0; _result = Image out { base_options = [ $font => font.value, $align => align.value ]; set_option name default value = [name => value], value != default = []; options = base_options ++ concat [ set_option $width 0 (to_real width), set_option $height 0 (to_real height), set_option $fontfile "" fontfile.value, if !fit then set_option $dpi 72 (to_real dpi) else [], set_option $spacing 0 (to_real spacing) ]; [out] = vips_call "text" [text.value] options; } } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } } ================================================ FILE: share/nip2/start/Magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate Magick.def 13-Apr-2014 snibgo Put "new image" items into sub-menu. New class VirtualPixlBack. 17-Apr-2014 snibgo Many small changes. A few new menu options. Created sub-menu for multi-input operations. 3-May-2014 jcupitt Put quotes around ( in shadow to help unix Last update: 17-Apr-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ // We don't need Noop. /*=== Magick_noop_item = class Menuaction "_Noop" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_testPar_item = class Menuaction "_TestPar" "no operation" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "( +clone ) +append ", "\"%s\"" ]; _result = Magick.system command x; } } /* Removed Read_item and Write_item, much better to use nip2 load/save image. * Plus they can load all libMagick formats anyway. */ // Put "new image" items into sub-menu Magick_NewImageMenu_item = class Menupullright "_New image" "make a new image" { Magick_newcanvas_item = class Menuaction "_Solid colour" "make image of solid colour" { action = class _result { _vislevel = 3; size = Magick.Size_widget; colour = Magick.generalcol_widget; command = Magick.command [ size._flag, "\"canvas:" ++ colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_builtin_item = class Menuaction "_Built-in image" "create a built-in image" { action = class _result { _vislevel = 3; builtin = Magick.builtin_widget; command = Magick.command [ builtin._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_gradient_item = class Menuaction "_Gradient" "make a linear gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; topColour = Magick.generalcol_widget; bottomColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"gradient:", topColour._flag, "-", bottomColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } Magick_hald_item = class Menuaction "_Hald-clut image" "create an identity hald-clut image" { action = class _result { _vislevel = 3; order = Expression "order" 8; command = Magick.command [ "hald:" ++ print order.expr, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_pattern_item = class Menuaction "_Pattern" "create pattern" { action = class _result { _vislevel = 3; size = Magick.Size_widget; pattern = Magick.pattern_widget; command = Magick.command [ size._flag, pattern._flag, "\"%s\"" ]; _result = Magick.system0 command; } } Magick_plasma_item = class Menuaction "_Plasma image" "create plasma image" { action = class _result { _vislevel = 3; size = Magick.Size_widget; // FIXME? ColourA-ColourB. // FIXME? Allow plasma:fractal? command = Magick.command [ size._flag, "plasma:", "\"%s\"" ]; _result = Magick.system0 command; } } Magick_radialgradient_item = class Menuaction "_Radial gradient" "make a radial gradient between two colours" { action = class _result { _vislevel = 3; size = Magick.Size_widget; innerColour = Magick.generalcol_widget; outerColour = Magick.generalcol_widget; command = Magick.command [ size._flag, concat ["\"radial-gradient:", innerColour._flag, "-", outerColour._flag, "\""], "\"%s\"" ]; _result = Magick.system0 command; } } } // end Magick_NewImageMenu_item Magick_MultiMenu_item = class Menupullright "_Multiple inputs" "make an image from multiple images" { Magick_composite_item = class Menuaction "_Composite" "composite two images (without mask)" { action x y = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_compositeMask_item = class Menuaction "_Composite masked" "composite two images (with mask)" { action x y z = class _result { _vislevel = 3; method = Magick.compose_widget; offsets = Magick.OffsetGeometry_widget; gravity = Magick.gravity_widget; command = Magick.command [ "\"%s\"", "\"%s\"", "\"%s\"", "-geometry", offsets._flag, gravity._flag, method._flag, "-composite", "\"%s\"" ]; _result = Magick.system3 command x y z; } } // FIXME: other operations like remap that take another image as arguments are: // mask (pointless?), texture, tile (pointless?) // FIXME: operations that take a filename that isn't an image: // cdl, profile Magick_clut_item = class Menuaction "_Clut" "replace values using second image as colour look-up table" { action x y = class _result { _vislevel = 3; // FIXME: uses -intensity "when mapping greyscale CLUT image to alpha channel if set by -channels" command = Magick.command [ "\"%s\"", "\"%s\"", "-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } Magick_haldclut_item = class Menuaction "_Hald clut" "replace values using second image as Hald colour look-up table" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "\"%s\"", "-hald-clut", "\"%s\"" ]; _result = Magick.system2 command x y; } } // Encipher and decipher: key files can be text or image files. Magick_encipher_item = class Menuaction "_Encipher/Decipher" "encipher or decipher an image image" { action x = class _result { _vislevel = 3; pathname = Pathname "Read key file" ""; isDecipher = Toggle "Decipher" false; command = Magick.command [ "\"%s\"", if pathname.value == "" then "" else ( ( if isDecipher then "-decipher " else "-encipher ") ++ ( "\"" ++ pathname.value ++ "\"" ) ), "\"%s\"" ]; _result = Magick.system command x; } } Magick_profile_item = class Menuaction "_Profile" "assigns/applies an ICC profile" { action x = class _result { _vislevel = 3; pathname1 = Pathname "Read profile file" ""; pathname2 = Pathname "Read profile file" ""; command = Magick.command [ "\"%s\"", if pathname1.value == "" then "" else ( "-profile " ++ "\"" ++ pathname1.value ++ "\""), if pathname2.value == "" then "" else ( "-profile " ++ "\"" ++ pathname2.value ++ "\""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_remap_item = class Menuaction "_Remap" "reduce colours to those in another image" { action x y = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-remap", "\"%s\"", "\"%s\"" ]; _result = Magick.system2 command x y; } } } // end Magick_MultiMenu_item Magick_image_type_item = class Menuaction "_Image Type" "change image type" { action x = class _result { _vislevel = 3; imagetype = Magick.imagetype_widget; command = Magick.command [ "\"%s\"", imagetype._flag, "\"%s\"" ]; _result = Magick.system command x; } } sep2 = Menuseparator; Magick_alpha_item = class Menuaction "_Alpha" "add/remove alpha channel" { action x = class _result { _vislevel = 3; alpha = Magick.alpha_widget; command = Magick.command [ "\"%s\"", alpha._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_annotate_item = class Menuaction "_Annotate" "add text annotation" { action x = class _result { _vislevel = 3; text = Magick.text_widget; font = Magick.Font_widget; geometry = Magick.AnnotGeometry_widget; gravity = Magick.gravity_widget; foreground = Magick.foreground_widget; undercol = Magick.undercol_widget; antialias = Magick.antialias_widget; command = Magick.command [ "\"%s\"", font._flag, antialias._flag, gravity._flag, foreground._flag, undercol._flag, "-annotate", geometry._flag, "\"" ++ text.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoGamma_item = class Menuaction "_AutoGamma" "automatic gamma" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-gamma", "\"%s\"" ]; _result = Magick.system command x; } } Magick_autoLevel_item = class Menuaction "_AutoLevel" "automatic level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-auto-level", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blurSharpMenu_item = class Menupullright "_Blur/Sharpen" "blur and sharpen" { Magick_adaptive_blur_item = class Menuaction "_Adaptive Blur" "blur less near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; // note: adaptive-blur doesn't regard VP. command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-adaptive-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_blur_item = class Menuaction "_Blur" "blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gaussianBlur_item = class Menuaction "_Gaussian Blur" "blur with a Gaussian operator" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-gaussian-blur", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_motionBlur_item = class Menuaction "_Motion Blur" "simulate motion blur" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; channels = Magick.ch_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-motion-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotationalBlur_item = class Menuaction "_RotationalBlur" "blur around the centre" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; angle = Scale "angle (degrees)" (-360) 360 20; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-radial-blur", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_selectiveBlur_item = class Menuaction "_Selective Blur" "blur where contrast is less than or equal to threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; threshold = Scale "Threshold (percent)" 0 100 50; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", intensity._flag, virtpixback._flag, channels._flag, "-selective-blur", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print threshold.value ++ "%%", "\"%s\"" ]; _result = Magick.system command x; } } sep1 = Menuseparator; Magick_adaptive_sharpen_item = class Menuaction "_Adaptive Sharpen" "sharpen more near edges" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-adaptive-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sharpen_item = class Menuaction "_Sharpen" "sharpen" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-sharpen", print radius.value ++ "x" ++ print sigma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_unsharpen_item = class Menuaction "_Unsharp" "sharpen with unsharp mask" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; channels = Magick.channels_widget; gain = Scale "Gain" (-10) 10 1; threshold = Scale "Threshold" 0 1 0.05; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, channels._flag, "-unsharp", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print gain.value ++ "+" ++ print threshold.value, "\"%s\"" ]; _result = Magick.system command x; } } } // end BlurSharpMenu_item Magick_border_item = class Menuaction "_Border" "add border of given colour" { action x = class _result { _vislevel = 3; compose = Magick.compose_widget; width = Expression "Width" 3; bordercol = Magick.bordercol_widget; command = Magick.command [ "\"%s\"", compose._flag, bordercol._flag, "-border", print width.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_brightCont_item = class Menuaction "_Brightness-contrast" "adjust the brightness and/or contrast" { action x = class _result { _vislevel = 3; bri = Scale "brightness" (-100) 100 0; con = Scale "contrast" (-100) 100 0; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-brightness-contrast", print bri.value ++ "x" ++ print con.value, "\"%s\"" ]; _result = Magick.system command x; } } // Note: canny requires ImageMagick 6.8.9-0 or later. Magick_canny_item = class Menuaction "_Canny" "detect a wide range of edges" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; lowPc = Scale "lower percent" 0 100 10; highPc = Scale "lower percent" 0 100 10; command = Magick.command [ "\"%s\"", "-canny", concat ["\"", print radius.value ++ "x" ++ print sigma.value ++ "+" ++ print lowPc.value ++ "%%+" ++ print highPc.value ++ "%%" ++ "\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_charcoal_item = class Menuaction "_Charcoal" "simulate a charcoal drawing" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; factor = Scale "factor" 0 50 1; command = Magick.command [ "\"%s\"", intensity._flag, "-charcoal", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_chop_item = class Menuaction "_Chop" "remove pixels from the interior" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-chop", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorize_item = class Menuaction "_Colorize" "colorize by given amount" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; val = Scale "value" 0 100 100; command = Magick.command [ "\"%s\"", foreground._flag, "-colorize", print val.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colors_item = class Menuaction "_Colors" "reduce number of colors" { action x = class _result { _vislevel = 3; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; quantize = Magick.colorspace_widget; colors = Expression "Colours" 3; command = Magick.command [ "\"%s\"", "-quantize", quantize._flag, "-treedepth", print treedepth.expr, dither._flag, "-colors", print colors.expr, "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: color-matrix? Magick_colorspace_item = class Menuaction "_Colourspace" "convert to arbitrary colourspace" { action x = class _result { _vislevel = 3; colsp = Magick.colorspace_widget; command = Magick.command [ "\"%s\"", "-colorspace", colsp._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_colorspaceGray_item = class Menuaction "_Colourspace gray" "convert to gray using given intensity method" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; command = Magick.command [ "\"%s\"", intensity._flag, "-colorspace gray", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrast_item = class Menuaction "_Contrast" "increase or reduce the contrast" { action x = class _result { _vislevel = 3; isReduce = Toggle "reduce contrast" false; command = Magick.command [ "\"%s\"", (if isReduce then "+" else "-") ++ "contrast", "\"%s\"" ]; _result = Magick.system command x; } } Magick_contrastStretch_item = class Menuaction "_Contrast stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-contrast-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: convolve (bias, kernel) Magick_crop_item = class Menuaction "_Crop" "cut out a rectangular region" { action x = class _result { _vislevel = 3; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", gravity._flag, "-crop", geometry._flag, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_deskew_item = class Menuaction "_Deskew" "straighten the image" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; // FIXME: toggle auto-crop? command = Magick.command [ "\"%s\"", "-deskew", "\"" ++ print threshold.value ++ "%%\"", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_despeckle_item = class Menuaction "_Despeckle" "reduce the speckles" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-despeckle", "\"%s\"" ]; _result = Magick.system command x; } } Magick_distort_item = class Menuaction "_Distort" "distort using a method and arguments" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; distort = Magick.distort_widget; args = String "Arguments" "1,0"; isPlus = Toggle "Extend to show entire image" false; command = Magick.command [ "\"%s\"", virtpixback._flag, (if isPlus then "+" else "-") ++ "distort", distort._flag, args.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_draw_item = class Menuaction "_Draw" "annotate with one or more graphic primitives" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; args = String "Arguments" "line 0,0 9,9 rectangle 10,10 20,20"; command = Magick.command [ "\"%s\"", foreground._flag, "-draw", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_edge_item = class Menuaction "_Edge" "detect edges" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-edge", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_emboss_item = class Menuaction "_Emboss" "emboss" { action x = class _result { _vislevel = 3; rad = Expression "Radius" 3; command = Magick.command [ "\"%s\"", "-emboss", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_enhance_item = class Menuaction "_Enhance" "enhance a noisy image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-enhance", "\"%s\"" ]; _result = Magick.system command x; } } Magick_equalize_item = class Menuaction "_Equalize" "equalize the histogram" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-equalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_evaluate_item = class Menuaction "_Evaluate" "evaluate an expression on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; operation = Magick.evaluate_widget; val = Expression "value" 5; isPc = Toggle "Value is percent" false; command = Magick.command [ "\"%s\"", channels._flag, operation._flag, print val.expr ++ if isPc then "%%" else "", "\"%s\"" ]; _result = Magick.system command x; } } Magick_extent_item = class Menuaction "_Extent" "set the image size and offset" { action x = class _result { _vislevel = 3; background = Magick.background_widget; compose = Magick.compose_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, background._flag, gravity._flag, "-extent", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_FlipFlopMenu_item = class Menupullright "_Flip/flop" "flip/flop/transverse/transpose" { Magick_flip_item = class Menuaction "_Flip vertically" "mirror upside-down" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flip", "\"%s\"" ]; _result = Magick.system command x; } } Magick_flop_item = class Menuaction "_Flop horizontally" "mirror left-right" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-flop", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transpose_item = class Menuaction "_Transpose" "mirror along the top-left to bottom-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transpose +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_transverse_item = class Menuaction "_Transverse" "mirror along the bottom-left to top-right diagonal" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-transverse +repage", "\"%s\"" ]; _result = Magick.system command x; } } } // end Magick_FlipFlopMenu_item Magick_floodfill_item = class Menuaction "_Floodfill" "recolour neighbours that match" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; fuzz = Magick.fuzz_widget; coordinate = Magick.coordinate_widget; // -draw "color x,y floodfill" command = Magick.command [ "\"%s\"", foreground._flag, "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-draw \" color", coordinate._flag, "floodfill \"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_frame_item = class Menuaction "_Frame" "surround with border or beveled frame" { action x = class _result { _vislevel = 3; border = Magick.bordercol_widget; compose = Magick.compose_widget; matte = Magick.mattecol_widget; geometry = Magick.FrameGeometry_widget; command = Magick.command [ "\"%s\"", compose._flag, border._flag, matte._flag, "-frame", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_function_item = class Menuaction "_Function" "evaluate a function on each pixel channel" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; function = Magick.function_widget; // FIXME: explain values; use sensible defaults. values = String "values" "0,0,0,0"; command = Magick.command [ "\"%s\"", channels._flag, "-function", function._flag, values.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_fx_item = class Menuaction "_Fx" "apply a mathematical expression" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; interpolate = Magick.interpolate_widget; virtpixback = Magick.VirtualPixelBack_widget; args = String "Expression" "u*1/2"; command = Magick.command [ "\"%s\"", channels._flag, interpolate._flag, virtpixback._flag, "-fx", concat ["\"", args.value, "\""], "\"%s\"" ]; _result = Magick.system command x; } } Magick_gamma_item = class Menuaction "_Gamma" "apply a gamma correction" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; gamma = Magick.gamma_widget; command = Magick.command [ "\"%s\"", channels._flag, "-gamma", print gamma.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradient_item = class Menuaction "_Gradient" "apply a linear gradient" { action x = class _result { _vislevel = 3; colourA = Magick.generalcol_widget; colourB = Magick.generalcol_widget; position = Option "colourA is at" [ "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"] 0; _baryArg = concat ["0,0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 0 = concat ["0,0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 1 = concat ["0,0,", colourA._flag, " %%[fx:w-1],0,", colourB._flag], position.value == 2 = concat ["0,0,", colourB._flag, " %%[fx:w-1],0,", colourA._flag], position.value == 3 = concat ["0,0,", colourA._flag, " %%[fx:w-1],%%[fx:h-1],", colourB._flag], position.value == 4 = concat ["%%[fx:w-1],0,", colourA._flag, " 0,%%[fx:h-1],", colourB._flag], position.value == 5 = concat ["%%[fx:w-1],0,", colourB._flag, " 0,%%[fx:h-1],", colourA._flag], position.value == 6 = concat ["0,0,", colourB._flag, " %%[fx:w-1],%%[fx:h-1],", colourA._flag], position.value == 7 = "dunno"; command = Magick.command [ "\"%s\"", "-sparse-color barycentric \"" ++ _baryArg ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_gradientCorn_item = class Menuaction "_Gradient corners" "apply a bilinear gradient between the corners" { action x = class _result { _vislevel = 3; colour_top_left = Magick.generalcol_widget; colour_top_right = Magick.generalcol_widget; colour_bottom_left = Magick.generalcol_widget; colour_bottom_right = Magick.generalcol_widget; command = Magick.command [ "\"%s\"", "-sparse-color bilinear \"" ++ "0,0," ++ colour_top_left._flag ++ ",%%[fx:w-1],0" ++ colour_top_right._flag ++ ",0,%%[fx:h-1]" ++ colour_bottom_left._flag ++ ",%%[fx:w-1],%%[fx:h-1]" ++ colour_bottom_right._flag ++ "\"", "+depth", "\"%s\"" ]; _result = Magick.system command x; } } Magick_histogram_item = class Menuaction "_Histogram" "make a histogram image" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-define histogram:unique-colors=false histogram:" ++ "\"%s\"" ]; _result = Magick.system command x; } } Magick_implode_item = class Menuaction "_Implode" "implode pixels about the center" { action x = class _result { _vislevel = 3; factor = Scale "factor" 0 20 1; interpolate = Magick.interpolate_widget; // FIXME: virtual-pixel? command = Magick.command [ "\"%s\"", interpolate._flag, "-implode", print factor.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_level_item = class Menuaction "_Level" "adjust the level of channels" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "black point" (-100) 200 0; wht = Scale "white point" (-100) 200 100; gam = Scale "gamma" 0 30 1; isPc = Toggle "Levels are percent" true; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level", "\"" ++ print blk.value ++ "," ++ print wht.value ++ (if isPc then "%%" else "") ++ "," ++ print gam.value ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_levelCols_item = class Menuaction "_Level colors" "adjust levels to given colours" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; colour_black = Magick.generalcol_widget; colour_white = Magick.generalcol_widget; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "level-colors", "\"" ++ colour_black._flag ++ "," ++ colour_white._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_linearStretch_item = class Menuaction "_Linear stretch" "stretches tones, making some black/white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; blk = Scale "percent to make black" 0 100 0; wht = Scale "percent to make white" 0 100 0; command = Magick.command [ "\"%s\"", channels._flag, "-linear-stretch", "\"" ++ print blk.value ++ "x" ++ print wht.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_magnify_item = class Menuaction "_Magnify" "double the size of the image with pixel art scaling" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-magnify", "\"%s\"" ]; _result = Magick.system command x; } } Magick_modulate_item = class Menuaction "_Modulate" "modulate brightness, saturation and hue" { action x = class _result { _vislevel = 3; modcolsp = Magick.ModColSp_widget; bright = Scale "brightness" 0 200 100; sat = Scale "saturation" 0 200 100; hue = Scale "hue" 0 200 100; command = Magick.command [ "\"%s\"", modcolsp._flag, "-modulate", print bright.value ++ "," ++ print sat.value ++ "," ++ print hue.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_monochrome_item = class Menuaction "_Monochrome" "transform to black and white" { action x = class _result { _vislevel = 3; // FIXME: also intensity? intensity = Magick.intensity_widget; treedepth = Expression "Treedepth" 8; dither = Magick.dither_widget; command = Magick.command [ "\"%s\"", intensity._flag, dither._flag, "-treedepth", print treedepth.expr, "-monochrome", "\"%s\"" ]; _result = Magick.system command x; } } Magick_morphology_item = class // See http://www.imagemagick.org/Usage/morphology/ Menuaction "_Morphology" "apply a morphological method" { action x = class _result { _vislevel = 3; method = Magick.morphmeth_widget; iter = Expression "Iterations (-1=repeat until done)" 1; kernel = Magick.kernel_widget; // FIXME: custom kernel eg "3x1+2+0:1,0,0" // width x height + offsx + offsy : {w*h values} // each value is 0.0 to 1.0 or "NaN" or "-" // kernel args, mostly float radius,scale. radius=0=default. default scale = 1.0 // but // ring takes: radius1, radius2, scale // rectangle and comet take: width x height + offsx + offsy // blur takes: radius x sigma // FIXME: for now, simply allow any string input. kernel_arg = String "Kernel arguments" ""; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-morphology", method._flag ++ ":" ++ print iter.expr, kernel._flag ++ (if kernel_arg.value == "" then "" else ":") ++ kernel_arg.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_negate_item = class Menuaction "_Negate" "negate" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", channels._flag, "-negate", "\"%s\"" ]; _result = Magick.system command x; } } Magick_addNoise_item = class Menuaction "_add Noise" "add noise" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; attenuate = Scale "attenuate" 0 1.0 1.0; noise = Magick.noise_widget; command = Magick.command [ "\"%s\"", channels._flag, "-attenuate", print attenuate.value, noise._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_normalize_item = class Menuaction "_Normalize" "normalize" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-normalize", "\"%s\"" ]; _result = Magick.system command x; } } Magick_opaque_item = class Menuaction "_Opaque" "change this colour to the fill colour" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; fill = Magick.foreground_widget; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", fill._flag, channels._flag, "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "opaque", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_paint_item = class Menuaction "_Paint" "simulate an oil painting" { action x = class _result { _vislevel = 3; rad = Expression "radius" 10; command = Magick.command [ "\"%s\"", "-paint", print rad.expr, "\"%s\"" ]; _result = Magick.system command x; } } /*=== FIXME Bug; remove for now. Polaroid_item = class Menuaction "_Polaroid" "simulate a polaroid picture" { action x = class _result { _vislevel = 3; angle = Scale "angle" (-90) 90 20; command = Magick.command [ "\"%s\"", "-polaroid", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } ===*/ Magick_posterize_item = class Menuaction "_Posterize" "reduce to (n) levels per channel" { action x = class _result { _vislevel = 3; levels = Expression "levels" 3; command = Magick.command [ "\"%s\"", "-posterize", print levels.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_raise_item = class Menuaction "_Raise" "lighten or darken image edges" { action x = class _result { _vislevel = 3; thk = Expression "Thickness" 3; command = Magick.command [ "\"%s\"", "-raise", print thk.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_resize_item = class Menuaction "_Resize" "resize to given width and height" { action x = class _result { _vislevel = 3; filter = Magick.filter_widget; type = Magick.ResizeType_widget; width = Scale "Width" 1 100 10; height = Scale "Height" 1 100 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", filter._flag, "-" ++ type._flag, "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "!") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_roll_item = class Menuaction "_Roll" "roll an image horizontally or vertically" { action x = class _result { _vislevel = 3; rollx = Expression "X" 3; rolly = Expression "Y" 3; command = Magick.command [ "\"%s\"", "-roll", (if rollx.expr >= 0 then "+" else "") ++ print rollx.expr ++ (if rolly.expr >= 0 then "+" else "") ++ print rolly.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_rotate_item = class Menuaction "_Rotate" "rotate" { action x = class _result { _vislevel = 3; angle = Scale "angle (degrees)" (-360) 360 20; virtpixback = Magick.VirtualPixelBack_widget; command = Magick.command [ "\"%s\"", virtpixback._flag, "+distort", "SRT", print angle.value, "+repage", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -segment, but cluster-threshold should be percentage of image area. Magick_sepia_item = class Menuaction "_Sepia tone" "simulate a sepia-toned photo" { action x = class _result { _vislevel = 3; threshold = Scale "Threshold (percent)" 0 100 80; command = Magick.command [ "\"%s\"", "-sepia-tone", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shade_item = class Menuaction "_Shade" "shade with a distant light source" { action x = class _result { _vislevel = 3; azimuth = Scale "Azimuth (degrees)" (-360) 360 0; elevation = Scale "Elevation (degrees)" 0 90 45; command = Magick.command [ "\"%s\"", "-shade", print azimuth.value ++ "x" ++ print elevation.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_shadow_item = class Menuaction "_Shadow" "simulate a shadow" { action x = class _result { _vislevel = 3; shadowCol = Magick.generalcol_widget; opacity = Scale "Opacity (percent)" 0 100 75; sigma = Scale "Sigma" 0 30 2; // FIXME: make offsets a single widget? offsx = Scale "X-offset" (-20) 20 4; offsy = Scale "Y-offset" (-20) 20 4; arePc = Toggle "offsets are percentages" false; // FIXME: raw operation creates page offset, which vips dislikes. // So we take this futher, compositing with source. command = Magick.command [ "\"%s\"", "\"(\" +clone", "-background", "\"" ++ shadowCol._flag ++ "\"", "-shadow", concat [ "\"", print opacity.value, "x", print sigma.value, (if offsx.value >= 0 then "+" else ""), print offsx.value, (if offsy.value >= 0 then "+" else ""), print offsy.value, (if arePc then "%%" else ""), "\"" ], "\")\" +swap -background None -layers merge", "+repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shave_item = class Menuaction "_Shave" "shave pixels from the edges" { action x = class _result { _vislevel = 3; width = Scale "Width" 0 50 10; height = Scale "Height" 0 50 10; isPc = Toggle "Width and height are percent" false; command = Magick.command [ "\"%s\"", "-shave", "\"" ++ print width.value ++ "x" ++ print height.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_shear_item = class Menuaction "_Shear" "shear along the x-axis and/or y-axis" { action x = class _result { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-shear", print shearX.expr ++ "x" ++ print shearY.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_sigmoid_item = class Menuaction "_Sigmoid" "increase or decrease mid-tone contrast sigmoidally" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; contrast = Scale "contrast" 0 30 3; midpoint = Scale "mid-point (percent)" 0 100 50; isInv = Toggle "Invert effect" false; command = Magick.command [ "\"%s\"", channels._flag, (if isInv then "+" else "-") ++ "sigmoidal-contrast", "\"" ++ print contrast.value ++ "x" ++ print midpoint.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_sketch_item = class Menuaction "_Sketch" "simulate a pencil sketch" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; angle = Scale "angle" (-360) 360 0; command = Magick.command [ "\"%s\"", "-sketch", print radius.value ++ "x" ++ print sigma.value ++ (if angle >= 0 then ("+" ++ print angle.value) else ""), "\"%s\"" ]; _result = Magick.system command x; } } Magick_solarize_item = class Menuaction "_Solarize" "negate all pixels above a threshold level" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-solarize", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } // FIXME: -sparse-color needs abitrary list of {x,y,colour}. Magick_splice_item = class Menuaction "_Splice" "splice a colour into the image" { action x = class _result { _vislevel = 3; background = Magick.background_widget; gravity = Magick.gravity_widget; geometry = Magick.WhxyGeometry_widget; command = Magick.command [ "\"%s\"", background._flag, gravity._flag, "-splice", geometry._flag, "\"%s\"" ]; _result = Magick.system command x; } } Magick_spread_item = class Menuaction "_Spread" "displace pixels by random amount" { action x = class _result { _vislevel = 3; virtpixback = Magick.VirtualPixelBack_widget; amount = Expression "Amount (pixels)" 5; command = Magick.command [ "\"%s\"", virtpixback._flag, "-spread", print amount.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_statistic_item = class Menuaction "_Statistic" "replace each pixel with statistic from neighbourhood" { action x = class _result { _vislevel = 3; width = Expression "Width" 5; height = Expression "Height" 5; statisticType = Magick.StatType_widget; command = Magick.command [ "\"%s\"", "-statistic", statisticType._flag, print width.expr ++ "x" ++ print height.expr, "\"%s\"" ]; _result = Magick.system command x; } } Magick_swirl_item = class Menuaction "_Swirl" "swirl around the centre" { action x = class _result { _vislevel = 3; angle = Magick.angle_widget; command = Magick.command [ "\"%s\"", "-swirl", print angle.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_thresholdMenu_item = class Menupullright "_Threshold" "make black or white" { Magick_threshold_item = class Menuaction "_Threshold" "apply black/white threshold" { action x = class _result { _vislevel = 3; intensity = Magick.intensity_widget; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", intensity._flag, channels._flag, "-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_blackThreshold_item = class Menuaction "_Black threshold" "where below threshold set to black" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-black-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_whiteThreshold_item = class Menuaction "_White threshold" "where above threshold set to white" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; threshold = Scale "Threshold (percent)" 0 100 50; command = Magick.command [ "\"%s\"", channels._flag, "-white-threshold", "\"" ++ print threshold.value ++ "%%\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_latThreshold_item = class Menuaction "_Local Adaptive Threshold" "where above average plus offset set to white, otherwise black" { action x = class _result { _vislevel = 3; width = Expression "Width" 10; height = Expression "Height" 10; offset = Scale "Offset (percent)" (-100) 100 0; // note: "-lat" doesn't respond to channels command = Magick.command [ "\"%s\"", "-lat", concat ["\"", print width.expr, "x", print height.expr, (if offset.value >= 0 then "+" else ""), print offset.value, "%%\"" ], "\"%s\"" ]; _result = Magick.system command x; } } Magick_randThreshold_item = class Menuaction "_Random Threshold" "between specified limits, apply random threshold" { action x = class _result { _vislevel = 3; channels = Magick.channels_widget; low = Scale "Low threshold" 0 100 10; high = Scale "High threshold" 0 100 90; isPc = Toggle "Thresholds are percent" true; command = Magick.command [ "\"%s\"", channels._flag, "-random-threshold", "\"" ++ print low.value ++ "x" ++ print high.value ++ (if isPc then "%%" else "") ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } } // end ThresholdMenu_item // Note: alternatives include: // convert in.tif -write mpr:TILE +delete -size 200x200 -background XXXX -tile-offset +30+30 tile:mpr:TILE out.tif Magick_tile_item = class Menuaction "_Tile" "fill given size with tiled image" { action x = class _result { _vislevel = 3; size = Magick.Size_widget; command = Magick.command [ size._flag, "tile:" ++ "\"%s\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_tint_item = class Menuaction "_Tint" "apply a tint" { action x = class _result { _vislevel = 3; foreground = Magick.foreground_widget; amount = Scale "amount (percent)" 0 100 50; command = Magick.command [ "\"%s\"", foreground._flag, "-tint", // snibgo note: although the amount is a percentage, it doesn't need "%" character. print amount.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_transparent_item = class Menuaction "_Transparent" "make this colour transparent" { action x = class _result { _vislevel = 3; changeColour = Magick.changeCol_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print changeColour.fuzz.value ++ "%%\"", (if changeColour.nonMatch then "+" else "-") ++ "transparent", "\"" ++ changeColour.colour._flag ++ "\"", "\"%s\"" ]; _result = Magick.system command x; } } Magick_trim_item = class Menuaction "_Trim" "trims away border" { action x = class _result { _vislevel = 3; fuzz = Magick.fuzz_widget; command = Magick.command [ "\"%s\"", "-fuzz", "\"" ++ print fuzz.value ++ "%%\"", "-trim +repage", "\"%s\"" ]; _result = Magick.system command x; } } Magick_uniqueCols_item = class Menuaction "_Unique colours" "discard all but one of any pixel color" { action x = class _result { _vislevel = 3; command = Magick.command [ "\"%s\"", "-unique-colors", "\"%s\"" ]; _result = Magick.system command x; } } Magick_vignette_item = class Menuaction "_Vignette" "soften the edges in vignette style" { action x = class _result { _vislevel = 3; radius = Magick.blur_rad_widget; sigma = Magick.sigma_widget; rx = Scale "Rolloff x (percent)" 0 100 10; ry = Scale "Rolloff y (percent)" 0 100 10; background = Magick.background_widget; command = Magick.command [ "\"%s\"", background._flag, "-vignette", print radius.value ++ "x" ++ print sigma.value ++ (if rx.value >= 0 then "+" else "") ++ print rx.value ++ (if ry.value >= 0 then "+" else "") ++ print ry.value, "\"%s\"" ]; _result = Magick.system command x; } } Magick_wave_item = class Menuaction "_Wave" "shear the columns into a sine wave" { action x = class _result { _vislevel = 3; amplitude = Scale "Amplitude (pixels)" 0 100 10; wavelength = Scale "Wavelength (pixels)" 0 100 10; command = Magick.command [ "\"%s\"", "-wave", print amplitude.value ++ "x" ++ print wavelength.value, "\"%s\"" ]; _result = Magick.system command x; } } ================================================ FILE: share/nip2/start/Makefile.am ================================================ startdir = $(pkgdatadir)/start start_DATA = \ Math.def \ Image.def \ Magick.def \ Colour.def \ Tasks.def \ Object.def \ Filter.def \ Matrix.def \ Widgets.def \ Histogram.def \ Preferences.ws \ _joe_extra.def \ _joe_utilities.def \ _convert.def \ _generate.def \ _list.def \ _predicate.def \ _stdenv.def \ _Object.def \ _magick.def \ _types.def EXTRA_DIST = $(start_DATA) ================================================ FILE: share/nip2/start/Math.def ================================================ Math_arithmetic_item = class Menupullright "_Arithmetic" "basic arithmetic for objects" { Add_item = class Menuaction "_Add" "add a and b" { action a b = map_binary add a b; } Subtract_item = class Menuaction "_Subtract" "subtract b from a" { action a b = map_binary subtract a b; } Multiply_item = class Menuaction "_Multiply" "multiply a by b" { action a b = map_binary multiply a b; } Divide_item = class Menuaction "_Divide" "divide a by b" { action a b = map_binary divide a b; } Remainder_item = class Menuaction "_Remainder" "remainder after integer division of a by b" { action a b = map_binary remainder a b; } sep1 = Menuseparator; Absolute_value_item = class Menuaction "A_bsolute Value" "absolute value of x" { action x = map_unary abs x; } Absolute_value_vector_item = class Menuaction "Absolute Value _Vector" "like Absolute Value, but treat pixels as vectors" { action x = map_unary abs_vec x; } Sign_item = class Menuaction "S_ign" "unit vector" { action x = map_unary sign x; } Negate_item = class Menuaction "_Negate" "multiply by -1" { action x = map_unary unary_minus x; } } Math_trig_item = class Menupullright "_Trigonometry" "trigonometry operations (all in degrees)" { Sin_item = class Menuaction "_Sine" "calculate sine x" { action x = map_unary sin x; } Cos_item = class Menuaction "_Cosine" "calculate cosine x" { action x = map_unary cos x; } Tan_item = class Menuaction "_Tangent" "calculate tangent x" { action x = map_unary tan x; } sep1 = Menuseparator; Asin_item = class Menuaction "Arc S_ine" "calculate arc sine x" { action x = map_unary asin x; } Acos_item = class Menuaction "Arc C_osine" "calculate arc cosine x" { action x = map_unary acos x; } Atan_item = class Menuaction "Arc T_angent" "calculate arc tangent x" { action x = map_unary atan x; } sep2 = Menuseparator; Rad_item = class Menuaction "_Degrees to Radians" "convert degrees to radians" { action x = map_unary rad x; } Deg_item = class Menuaction "_Radians to Degrees" "convert radians to degrees" { action x = map_unary deg x; } sep3 = Menuseparator; Angle_range_item = class Menuaction "Angle i_n Range" "is angle within t degrees of r, mod 360" { action t r angle = clock (max - angle) < 2*r { max = clock (t + r); clock a = a + 360, a < 0; = a - 360, a >= 360; = a; } } } Math_log_item = class Menupullright "_Log" "logarithms and anti-logs" { Exponential_item = class Menuaction "_Exponential" "calculate e ** x" { action x = map_unary (power e) x; } Log_natural_item = class Menuaction "Natural _Log" "log base e of x" { action x = map_unary log x; } sep1 = Menuseparator; Exponential10_item = class Menuaction "E_xponential base 10" "calculate 10 ** x" { action x = map_unary (power 10) x; } Log10_item = class Menuaction "L_og Base 10" "log base 10 of x" { action x = map_unary log10 x; } sep2 = Menuseparator; Raise_to_power_item = class Menuaction "_Raise to Power" "calculate x ** y" { action x y = map_binary power x y; } } Math_complex_item = class Menupullright "_Complex" "operations on complex numbers and images" { Complex_extract = class Menupullright "_Extract" "extract fields from complex" { Real_item = class Menuaction "_Real" "extract real part of complex" { action in = map_unary re in; } Imaginary_item = class Menuaction "_Imaginary" "extract imaginary part of complex" { action in = map_unary im in; } } Complex_build_item = class Menuaction "_Build" "join a and b to make a complex" { action a b = map_binary comma a b; } sep1 = Menuseparator; Polar_item = class Menuaction "_Polar" "convert real and imag to amplitude and phase" { action a = map_unary polar a; } Rectangular_item = class Menuaction "_Rectagular" ("convert (amplitude, phase) image to rectangular " ++ "coordinates") { action x = map_unary rectangular x; } sep2 = Menuseparator; Conjugate_item = class Menuaction "_Conjugate" "invert imaginary part" { action x = map_unary conj x; } } Math_boolean_item = class Menupullright "_Boolean" "bitwise boolean operations for integer objects" { And_item = class Menuaction "_AND" "bitwise AND of a and b" { action a b = map_binary bitwise_and a b; } Or_item = class Menuaction "_OR" "bitwise OR of a and b" { action a b = map_binary bitwise_or a b; } Eor_item = class Menuaction "_XOR" "bitwise exclusive or of a and b" { action a b = map_binary eor a b; } Not_item = class Menuaction "_NOT" "invert a" { action a = map_unary not a; } sep1 = Menuseparator; Right_shift_item = class Menuaction "Shift _Right" "shift a right by b bits" { action a b = map_binary right_shift a b; } Left_shift_item = class Menuaction "Shift _Left" "shift a left by b bits" { action a b = map_binary left_shift a b; } sep2 = Menuseparator; If_then_else_item = class Menuaction "_If Then Else" "b where a is non-zero, c elsewhere" { action a b c = map_trinary ite a b c { // can't use if_then_else, we need a true trinary ite a b c = if a then b else c; } } Bandand_item = Image_band_item.Bandand_item; Bandor_item = Image_band_item.Bandor_item; } Math_relational_item = class Menupullright "R_elational" "comparison operations" { Equal_item = class Menuaction "_Equal to" "test a equal to b" { action a b = map_binary equal a b; } Not_equal_item = class Menuaction "_Not Equal to" "test a not equal to b" { action a b = map_binary not_equal a b; } sep1 = Menuseparator; More_item = class Menuaction "_More Than" "test a strictly greater than b" { action a b = map_binary more a b; } Less_item = class Menuaction "_Less Than" "test a strictly less than b" { action a b = map_binary less a b; } sep2 = Menuseparator; More_equal_item = class Menuaction "M_ore Than or Equal to" "test a greater than or equal to b" { action a b = map_binary more_equal a b; } Less_equal_item = class Menuaction "L_ess Than or Equal to" "test a less than or equal to b" { action a b = map_binary less_equal a b; } } Math_list_item = class Menupullright "L_ist" "operations on lists" { Head_item = class Menuaction "_Head" "first element in list" { action x = map_unary hd x; } Tail_item = class Menuaction "_Tail" "list without the first element" { action x = map_unary tl x; } Last_item = class Menuaction "_Last" "last element in list" { action x = map_unary last x; } Init_item = class Menuaction "_Init" "list without the last element" { action x = map_unary init x; } sep1 = Menuseparator; Reverse_item = class Menuaction "_Reverse" "reverse order of elements in list" { action x = map_unary reverse x; } Sort_item = class Menuaction "_Sort" "sort list into ascending order" { action x = map_unary sort x; } Make_set_item = class Menuaction "_Make Set" "remove duplicates from list" { action x = map_unary mkset equal x; } Transpose_list_item = class Menuaction "Tr_anspose" "exchange rows and columns in a list of lists" { action x = map_unary transpose x; } Concat_item = class Menuaction "_Concat" "flatten a list of lists into a single list" { action l = map_unary concat l; } sep2 = Menuseparator; Length_item = class Menuaction "L_ength" "find the length of list" { action x = map_unary len x; } Subscript_item = class Menuaction "S_ubscript" "return element n from list (index from zero)" { action n x = map_binary subscript n x; } Take_item = class Menuaction "_Take" "take the first n elements of list x" { action n x = map_binary take n x; } Drop_item = class Menuaction "_Drop" "drop the first n elements of list x" { action n x = map_binary drop n x; } sep3 = Menuseparator; Join_item = class Menuaction "_Join" "join two lists end to end" { action a b = map_binary join a b; } Difference_item = class Menuaction "_Difference" "difference of two lists" { action a b = map_binary difference a b; } Cons_item = class Menuaction "C_ons" "put element a on the front of list x" { action a x = map_binary cons a x; } Zip_item = class Menuaction "_Zip" "join two lists, pairwise" { action a b = map_binary zip2 a b; } } Math_round_item = class Menupullright "_Round" "various rounding operations" { /* smallest integral value not less than x */ Ceil_item = class Menuaction "_Ceil" "smallest integral value not less than x" { action x = map_unary ceil x; } Floor_item = class Menuaction "_Floor" "largest integral value not greater than x" { action x = map_unary floor x; } Rint_item = class Menuaction "_Round to Nearest" "round to nearest integer" { action x = map_unary rint x; } } Math_fourier_item = class Menupullright "_Fourier" "Fourier transform" { Forward_item = class Menuaction "_Forward" "fourier transform of image" { action a = map_unary (rotquad @ fwfft) a; } Reverse_item = class Menuaction "_Reverse" "inverse fourier transform of image" { action a = map_unary (invfft @ rotquad) a; } Rotate_quadrants_item = class Menuaction "Rotate _Quadrants" "rotate quadrants" { action a = map_unary rotquad a; } } Math_stats_item = class Menupullright "_Statistics" "measure various statistics of objects" { Value_item = class Menuaction "_Value" "value of point in object" { action a = class _result { _vislevel = 3; position = Expression "Coordinate" (0, 0); _result = map_binary point position.expr a; } } Mean_item = class Menuaction "_Mean" "arithmetic mean value" { action a = map_unary mean a; } Gmean_item = class Menuaction "_Geometric Mean" "geometric mean value" { action a = map_unary meang a; } Zmean_item = class Menuaction "_Zero-excluding Mean" "mean value of non-zero elements" { action a = map_unary meanze a; } Deviation_item = class Menuaction "_Standard Deviation" "standard deviation of object" { action a = map_unary deviation a; } Zdeviation_item = class Menuaction "Z_ero-excluding Standard Deviation" "standard deviation of non-zero elements" { action a = map_unary deviationze a; } Skew_item = class Menuaction "S_kew" "skew of image or list or vector" { action a = map_unary skew a; } Kurtosis_item = class Menuaction "Kurtosis" "kurtosis of image or list or vector" { action a = map_unary kurtosis a; } Stats_item = class Menuaction "Ma_ny Stats" "calculate many stats in a single pass" { action a = map_unary stats a; } sep1 = Menuseparator; Max_item = class Menuaction "M_aximum" "maximum of object" { action a = map_unary max a; } Min_item = class Menuaction "M_inimum" "minimum of object" { action a = map_unary min a; } Maxpos_item = class Menuaction "_Position of Maximum" "position of maximum in object" { action a = map_unary maxpos a; } Minpos_item = class Menuaction "P_osition of Minimum" "position of minimum in object" { action a = map_unary minpos a; } Gravity_item = class Menuaction "Centre of _Gravity" "position of centre of gravity of histogram" { action a = map_unary gravity a; } sep2 = Menuseparator; Count_set_item = class Menuaction "_Non-zeros" "number of non-zero elements in object" { action a = map_unary cset a { cset i = (mean (i != 0) * i.width * i.height) / 255; } } Count_clear_item = class Menuaction "_Zeros" "number of zero elements in object" { action a = map_unary cclear a { cclear i = (mean (i == 0) * i.width * i.height) / 255; } } Count_edges_item = class Menuaction "_Edges" "count average edges across or down image" { action x = class _result { _vislevel = 3; edge = Option "Count" [ "Horizontal lines", "Vertical lines" ] 0; _result = map_unary process x { process image = Number (edge.labels?edge) (im_cntlines image.value edge.value); } } } sep3 = Menuseparator; Linear_regression_item = class Menuaction "_Linear Regression" "fit a line to a set of points" { action xes yes = linreg xes yes; } Weighted_linear_regression_item = class Menuaction "_Weighted Linear Regression" "fit a line to a set of points and deviations" { action xes yes devs = linregw xes yes devs; } Cluster_item = class Menuaction "_Cluster" "cluster a list of numbers" { action l = class { _vislevel = 3; thresh = Expression "Threshold" 10; [_r, _w] = cluster thresh.expr l; result = _r; weights = _w; } } } Math_base_item = class Menupullright "Bas_e" "convert number bases" { Hexadecimal_item = class Menuaction "_Hexadecimal" "convert to hexadecimal (base 16)" { action a = map_unary (print_base 16) a; } Binary_item = class Menuaction "_Binary" "convert to binary (base 2)" { action a = map_unary (print_base 2) a; } Octal_item = class Menuaction "_Octal" "convert to octal (base 8)" { action a = map_unary (print_base 8) a; } } ================================================ FILE: share/nip2/start/Matrix.def ================================================ Matrix_build_item = class Menupullright "_New" "make a new matrix of some sort" { Plain_item = class Menuaction "_Plain" "make a new plain matrix widget" { action = Matrix (identity_matrix 3); } Convolution_item = class Menuaction "_Convolution" "make a new convolution matrix widget" { action = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; } Recombination_item = class Menuaction "_Recombination" "make a new recombination matrix widget" { action = Matrix_rec (identity_matrix 3); } Morphology_item = class Menuaction "_Morphology" "make a new morphology matrix widget" { action = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; } sep1 = Menuseparator; Matrix_identity_item = class Menuaction "_Identity" "make an identity matrix" { action = identity (identity_matrix 5); identity v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix (identity_matrix (to_real s)), to_real s != len v; = Matrix v; Matrix_vips value scale offset filename display = identity value; } } Matrix_series_item = class Menuaction "_Series" "make a series" { action = series (mkseries 0 1 5); mkseries s t e = transpose [[to_real s, to_real s + to_real t .. to_real e]]; series v = class _result { _vislevel = 3; _s = v?0?0; _t = v?1?0 - v?0?0; _e = (last v)?0; s = Expression "Start value" _s; t = Expression "Step by" _t; e = Expression "End value" _e; _result = Matrix (mkseries s t e), to_real s != _s || to_real t != _t || to_real e != _e = Matrix v; Matrix_vips value scale offset filename display = series value; } } Matrix_square_item = class Menuaction "_Square" "make a square matrix" { action = square (mksquare 5); mksquare s = replicate s (take s [1, 1 ..]); square v = class _result { _vislevel = 3; s = Expression "Size" (len v); _result = Matrix_con (sum v) 0 v, len v == to_real s = Matrix_con (sum m) 0 m { m = mksquare (to_real s); } Matrix_vips value scale offset filename display = square value; } } Matrix_circular_item = class Menuaction "_Circular" "make a circular matrix" { action = circle (mkcircle 3); mkcircle r = map2 (map2 pyth) xes yes { line = [-r .. r]; xes = replicate (2 * r + 1) line; yes = transpose xes; pyth a b = 1, (a**2 + b**2) ** 0.5 <= r = 0; } circle v = class _result { _vislevel = 3; r = Expression "Radius" ((len v - 1) / 2); _result = Matrix_con (sum v) 0 v, len v == r.expr * 2 + 1 = Matrix_con (sum m) 0 m { m = mkcircle (to_real r); } Matrix_vips value scale offset filename display = circle value; } } Matrix_gaussian_item = class Menuaction "_Gaussian" "make a gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1; ma = Scale "Minimum amplitude" 0 1 0.2; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_gauss_imask, integer = im_gauss_dmask; } } } Matrix_laplacian_item = class Menuaction "_Laplacian" "make the Laplacian of a Gaussian matrix" { action = class _result { _vislevel = 3; s = Scale "Sigma" 0.001 10 1.5; ma = Scale "Minimum amplitude" 0 1 0.1; integer = Toggle "Integer" false; _result = fn s.value ma.value { fn = im_log_imask, integer = im_log_dmask; } } } } Matrix_to_matrix_item = class Menuaction "Con_vert to Matrix" "convert anything to a matrix" { action x = to_matrix x; } #separator Matrix_extract_item = class Menupullright "_Extract" "extract rows or columns from a matrix" { Rows_item = class Menuaction "_Rows" "extract rows" { action x = class _result { _vislevel = 3; first = Expression "Extract from row" 0; number = Expression "Extract this many rows" 1; _result = map_unary process x { process x = extract_area 0 first (get_width x) number x; } } } Columns_item = class Menuaction "_Columns" "extract columns" { action x = class _result { _vislevel = 3; first = Expression "Extract from column" 0; number = Expression "Extract this many columns" 1; _result = map_unary process x { process mat = extract_area first 0 number (get_height x) x; } } } Area_item = class Menuaction "_Area" "extract area" { action x = class _result { _vislevel = 3; left = Expression "First column" 0; top = Expression "First row" 0; width = Expression "Number of columns" 1; height = Expression "Number of rows" 1; _result = map_unary process x { process mat = extract_area left top width height x; } } } Diagonal_item = class Menuaction "_Diagonal" "extract diagonal" { action x = class _result { _vislevel = 3; which = Option "Extract" [ "Leading Diagonal", "Trailing Diagonal" ] 0; _result = map_unary process x { process mat = mat.Matrix_base (map2 extr [0..] mat.value), which == 0 = mat.Matrix_base (map2 extr [mat.width - 1, mat.width - 2 .. 0] mat.value); extr n l = [l?n]; } } } } Matrix_insert_item = class Menupullright "_Insert" "insert rows or columns into a matrix" { // make a new 8-bit uchar image of wxh with pixels set to p // use to generate new cells newim w h p = image_new w h 1 Image_format.UCHAR Image_coding.NOCODING Image_type.B_W p 0 0; Rows_item = class Menuaction "_Rows" "insert rows" { action x = class _result { _vislevel = 3; first = Expression "Insert at row" 0; number = Expression "Insert this many rows" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_tb (concat [top, new, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim w number item.expr)]; bottom = [extract_area 0 f w (h - f) x], f < h = []; f = to_real first; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "insert columns" { action x = class _result { _vislevel = 3; first = Expression "Insert at column" 0; number = Expression "Insert this many columns" 1; item = Expression "Set new cells to" 0; _result = map_unary process x { process x = foldl1 join_lr (concat [left, new, right]) { left = [extract_area 0 0 f h x], f > 0 = []; new = [(if is_Matrix x then to_matrix else id) (newim number h item.expr)]; right = [extract_area f 0 (w - f) h x], f < w = []; f = to_real first; w = get_width x; h = get_height x; } } } } } Matrix_delete_item = class Menupullright "_Delete" "delete rows or columns from a matrix" { // remove number of items, starting at first delete first number l = take (to_real first) l ++ drop (to_real first + to_real number) l; Rows_item = class Menuaction "_Rows" "delete rows" { action x = class _result { _vislevel = 3; first = Expression "Delete from row" 0; number = Expression "Delete this many rows" 1; _result = map_unary process x { process x = foldl1 join_tb (concat [top, bottom]) { top = [extract_area 0 0 w f x], f > 0 = []; bottom = [extract_area 0 b w (h - b) x], b < h = []; f = to_real first; n = to_real number; b = f + n; w = get_width x; h = get_height x; } } } } Columns_item = class Menuaction "_Columns" "delete columns" { action x = class _result { _vislevel = 3; first = Expression "Delete from column" 0; number = Expression "Delete this many columns" 1; _result = map_unary process x { process x = foldl1 join_lr (concat [left, right]) { left = [extract_area 0 0 f h x], f > 0 = []; right = [extract_area r 0 (w - r) h x], r < w = []; f = to_real first; n = to_real number; r = f + n; w = get_width x; h = get_height x; } } } } } Matrix_join = class Menupullright "_Join" "join two matricies" { Left_right_item = class Menuaction "_Left to Right" "join two matricies left-right" { action a b = map_binary join_lr a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "joiin two matricies top-bottom" { action a b = map_binary join_tb a b; } } Matrix_rotate_item = class Menupullright "_Rotate" "clockwise rotation by fixed angles" { rot90 = Image_transform_item.Rotate_item.Fixed_item.Rot90_item; rot180 = Image_transform_item.Rotate_item.Fixed_item.Rot180_item; rot270 = Image_transform_item.Rotate_item.Fixed_item.Rot270_item; Matrix_rot45_item = class Menuaction "_45 Degrees" "45 degree rotate (square, odd-length-sides only)" { action x = map_unary rot45 x; } } Matrix_flip_item = Image_transform_item.Flip_item; Matrix_sort_item = class Menuaction "_Sort" "sort by column or row" { action x = class _result { _vislevel = 3; o = Option (_ "Orientation") [ _ "Sort by column", _ "Sort by row" ] 0; i = Expression (_ "Sort on index") 0; d = Option (_ "Direction") [ _ "Ascending", _ "Descending" ] 0; _result = map_unary sort x { idx = to_real i; pred a b = a?idx <= b?idx, d == 0 = a?idx >= b?idx; sort x = (x.Matrix_base @ sortc pred) x.value, o == 0 = (x.Matrix_base @ transpose @ sortc pred @ transpose) x.value; } } } #separator Matrix_invert_item = class Menuaction "In_vert" "calculate inverse matrix" { action x = map_unary (converse power (-1)) x; } Matrix_transpose_item = class Menuaction "_Transpose" "swap rows and columns" { action x = map_unary transpose x; } #separator Matrix_plot_scatter_item = class Menuaction "_Plot Scatter" "plot a scatter graph of a matrix of [x,y1,y2,..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _vislevel = 3; auto = Toggle "Auto Range" true; xmin = Expression "X range minimum" 0; xmax = Expression "X range maximum" 1; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options ((x2b @ get_image @ to_image) x) { options = [$style => Plot_style.POINT, $format => Plot_format.XYYY] ++ range; range = [], auto = [$xmin => xmin.expr, $xmax => xmax.expr, $ymin => ymin.expr, $ymax => ymax.expr]; // matrix to image makes a 1-band mxn image // we need to put columns into bands x2b im = bandjoin (map extract_col [0 .. w - 1]) { w = get_width im; h = get_height im; b = get_bands im; extract_col x = extract_area x 0 1 h im; } } } } Matrix_plot_item = Hist_plot_item; Matrix_buildlut_item = class Menuaction "_Build LUT From Scatter" "make a lookup table from a matrix of [x,y1,y2..] coordinates" { action x = class _result { _check_args = [ [x, "x", check_Matrix] ]; _result = buildlut x; } } ================================================ FILE: share/nip2/start/Object.def ================================================ Object_duplicate_item = class Menuaction "_Duplicate" "take a copy of an object" { action x = map_unary copy x; } #separator Object_list_to_group_item = class Menuaction "_List to Group" "turn a list of objects into a group" { action x = to_group x; } Object_group_to_list_item = class Menuaction "_Group to List" "turn a group into a list of objects" { action x = to_list x; } #separator Object_break_item = class Menuaction "_Break Up Object" "break an object into a list of components" { action x = map_unary break x { break x = bandsplit x, is_Image x = map Vector x.value, is_Matrix x = x.value, is_Vector x || is_Real x = error "Breakup: not Image/Matrix/Vector/Real"; } } Object_assemble_item = class Menuaction "_Assemble Objects" "assemble a list of objects into a single object" { action x = map_unary ass x { ass x = [], x == [] = Vector x, is_real_list x = Matrix x, is_matrix x = bandjoin x, is_listof is_Image x = Vector (map get_value x), is_listof is_Real x = Matrix (map get_value x), is_listof is_Vector x = error "Assemble: not list of Image/Vector/Real/image/real"; } } ================================================ FILE: share/nip2/start/Preferences.ws ================================================ ================================================ FILE: share/nip2/start/Tasks.def ================================================ Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Kernel_linear xfactor yfactor scale_im; _offset_im = resize Kernel_linear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [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], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; } ================================================ FILE: share/nip2/start/Widgets.def ================================================ Widget_slider_item = class Menuaction "_Scale" "make a new scale widget" { icon = "nip-slider-16.png"; action = Scale "untitled scale" 0 255 128; } Widget_toggle_item = class Menuaction "_Toggle" "make a new toggle widget" { action = Toggle "untitled toggle" false; } Widget_option_item = class Menuaction "_Option" "make a new option widget" { action = Option "untitled option" ["option0", "option1"] 0; } Widget_string_item = class Menuaction "St_ring" "make a new string widget" { action = String "Enter a string" "sample text"; } Widget_number_item = class Menuaction "_Number" "make a new number widget" { action = Number "Enter a number" 42; } Widget_expression_item = class Menuaction "_Expression" "make a new expression widget" { action = Expression "Enter an expression" 42; } Widget_pathname_item = class Menuaction "_File Chooser" "make a new file chooser widget" { action = Pathname "Pick a file" "$VIPSHOME/share/$PACKAGE/data/print_test_image.v"; } Widget_font_item = class Menuaction "F_ont Chooser" "make a new font chooser widget" { action = Fontname "Pick a font" Workspaces.Preferences.PAINTBOX_FONT; } Widget_clock_item = class Menuaction "_Clock" "make a new clock widget" { action = Clock 1 1; } ================================================ FILE: share/nip2/start/_Object.def ================================================ /* Lots of little arg checks. Global for convenience. */ check_any = [(const true), _ "any"]; check_bool = [is_bool, _ "boolean"]; check_real = [is_real, _ "real"]; check_ureal = [is_ureal, _ "unsigned real"]; check_preal = [is_preal, _ "positive real"]; check_list = [is_list, _ "list"]; check_real_list = [is_real_list, _ "list of real"]; check_string = [is_string, _ "string"]; check_string_list = [is_string_list, _ "list of string"]; check_int = [is_int, _ "integer"]; check_uint = [is_uint, _ "unsigned integer"]; check_pint = [is_pint, _ "positive integer"]; check_matrix = [is_matrix, _ "rectangular array of real"]; check_matrix_display = [Matrix_display.is_display, _ "0|1|2|3"]; check_image = [is_image, _ "image"]; check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"]; check_instance name = [is_instanceof name, name]; check_Image = check_instance "Image"; check_Matrix = [is_Matrix, _ "Matrix"]; check_colour_space = [is_colour_space, join_sep "|" Image_type.colour_spaces.names]; check_rectangular = [is_rectangular, _ "rectangular [[*]]"]; check_Guide = [is_Guide, _ "HGuide|VGuide"]; check_Colour = check_instance (_ "Colour"); check_Mark = check_instance (_ "Mark"); /* Check a set of args to a class. Two members to look at: _check_args and * _check_all. * * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]] * same number of lines as there are args * * stuff like "arg 2 must be real" * * - each line in _check_all is [test, "description"] * any number of lines * * stuff like "to must be greater than from" * * generate an error dialog with a helpful message on failure. * * Have as a separate function to try to keep the size of _Object down. */ check_args x = error message, badargs != [] || badalls != [] = x { argcheck = x._check_args; allcheck = x._check_all; // indent string indent = " "; // test for a condition in a check line fails test_fail x = ! x?0; // set of failed argcheck indexes badargs = map (extract 1) (filter test_fail (zip2 (map testarg argcheck) [0..])) { testarg x = x?2?0 x?0; } // set of failed allcheck indexes badalls = map (extract 1) (filter test_fail (zip2 (map hd allcheck) [0..])); // the error message message = _ "bad properties for " ++ "\"" ++ x.name ++ "\"\n" ++ argmsg ++ allmsg ++ "\n" ++ _ "where" ++ "\n" ++ arg_types ++ extra; // make the failed argcheck messages ... eg. ""value" should be // real, you passed " etc. argmsg = concat (map fmt badargs) { fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++ _ " should be of type " ++ argcheck?n?2?1 ++ ", " ++ _ "you passed" ++ ":\n" ++ indent ++ indent ++ print argcheck?n?0 ++ "\n"; } // make the failed allcheck messages ... eg "condition failed: // x < y" ... don't make a message if any typechecks have // failed, as we'll probably error horribly allmsg = [], badargs != [] = concat (map fmt badalls) ++ _ "you passed" ++ "\n" ++ concat (map fmt_arg argcheck) { fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n"; fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n"; } // make arg type notes arg_types = join_sep "\n" (map fmt argcheck) { fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1; } // extra bit at the bottom, if we have any conditions extra = [], allcheck == [] = "\n" ++ _ "and" ++ "\n" ++ all_desc; // make a list of all the allcheck descriptions, with a few // spaces in front all_desc_list = map (join indent @ extract 1) allcheck; // join em up to make a set of condition notes all_desc = join_sep "\n" all_desc_list; } /* Operator overloading stuff. */ Operator_type = class { ARITHMETIC = 1; // eg. add RELATIONAL = 2; // eg. less COMPOUND = 3; // eg. max/mean/etc. COMPOUND_REWRAP = 4; // eg. transpose } Operator op_name fn type symmetric = class { } /* Form the converse of an Operator. */ oo_converse op = Operator (converse_name op.op_name) (converse op.fn) op.type op.symmetric { converse_name x = init x, last x == last "'" = x ++ "'"; } /* Given an operator name, look up the definition. */ oo_binary_lookup op_name = matches?0, matches != [] = error (_ "unknown binary operator" ++ ": " ++ print op_name) { operator_table = [ Operator "add" add Operator_type.ARITHMETIC true, Operator "subtract" subtract Operator_type.ARITHMETIC false, Operator "remainder" remainder Operator_type.ARITHMETIC false, Operator "power" power Operator_type.ARITHMETIC false, Operator "subscript" subscript Operator_type.ARITHMETIC false, Operator "left_shift" left_shift Operator_type.ARITHMETIC false, Operator "right_shift" right_shift Operator_type.ARITHMETIC false, Operator "divide" divide Operator_type.ARITHMETIC false, Operator "join" join Operator_type.ARITHMETIC false, Operator "multiply" multiply Operator_type.ARITHMETIC true, Operator "logical_and" logical_and Operator_type.ARITHMETIC true, Operator "logical_or" logical_or Operator_type.ARITHMETIC true, Operator "bitwise_and" bitwise_and Operator_type.ARITHMETIC true, Operator "bitwise_or" bitwise_or Operator_type.ARITHMETIC true, Operator "eor" eor Operator_type.ARITHMETIC true, Operator "comma" comma Operator_type.ARITHMETIC false, Operator "if_then_else" if_then_else Operator_type.ARITHMETIC false, Operator "equal" equal Operator_type.RELATIONAL true, Operator "not_equal" not_equal Operator_type.RELATIONAL true, Operator "less" less Operator_type.RELATIONAL false, Operator "less_equal" less_equal Operator_type.RELATIONAL false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Given an operator name, look up a function that implements that * operator. */ oo_unary_lookup op_name = matches?0, matches != [] = error (_ "unknown unary operator" ++ ": " ++ print op_name) { operator_table = [ /* Operators. */ Operator "cast_signed_char" cast_signed_char Operator_type.ARITHMETIC false, Operator "cast_unsigned_char" cast_unsigned_char Operator_type.ARITHMETIC false, Operator "cast_signed_short" cast_signed_short Operator_type.ARITHMETIC false, Operator "cast_unsigned_short" cast_unsigned_short Operator_type.ARITHMETIC false, Operator "cast_signed_int" cast_signed_int Operator_type.ARITHMETIC false, Operator "cast_unsigned_int" cast_unsigned_int Operator_type.ARITHMETIC false, Operator "cast_float" cast_float Operator_type.ARITHMETIC false, Operator "cast_double" cast_double Operator_type.ARITHMETIC false, Operator "cast_complex" cast_complex Operator_type.ARITHMETIC false, Operator "cast_double_complex" cast_double_complex Operator_type.ARITHMETIC false, Operator "unary_minus" unary_minus Operator_type.ARITHMETIC false, Operator "negate" negate Operator_type.RELATIONAL false, Operator "complement" complement Operator_type.ARITHMETIC false, Operator "unary_plus" unary_plus Operator_type.ARITHMETIC false, /* Built in projections. */ Operator "re" re Operator_type.ARITHMETIC false, Operator "im" im Operator_type.ARITHMETIC false, Operator "hd" hd Operator_type.ARITHMETIC false, Operator "tl" tl Operator_type.ARITHMETIC false, /* Maths builtins. */ Operator "sin" sin Operator_type.ARITHMETIC false, Operator "cos" cos Operator_type.ARITHMETIC false, Operator "tan" tan Operator_type.ARITHMETIC false, Operator "asin" asin Operator_type.ARITHMETIC false, Operator "acos" acos Operator_type.ARITHMETIC false, Operator "atan" atan Operator_type.ARITHMETIC false, Operator "log" log Operator_type.ARITHMETIC false, Operator "log10" log10 Operator_type.ARITHMETIC false, Operator "exp" exp Operator_type.ARITHMETIC false, Operator "exp10" exp10 Operator_type.ARITHMETIC false, Operator "ceil" ceil Operator_type.ARITHMETIC false, Operator "floor" floor Operator_type.ARITHMETIC false ]; matches = filter test_name operator_table; test_name x = x.op_name == op_name; } /* Find the matching methods in a method table. */ oo_method_lookup table = map (extract 0) (filter (extract 1) table); /* A binary op: a is a class, b may be a class ... eg. "add" a b two obvious ways to find a method: - a.oo_binary_search "add" (+) b - b.oo_binary_search "add'" (converse (+)) a, is_class b if these fail but op is a symmetric operator (eg. a + b == b + a), we can also try reversing the args - a.oo_binary_search "add'" (converse (+)) b - b.oo_binary_search "add" (+) a, is_class b if those fail as well, but this is ==, do pointer equals as a fallback */ oo_binary_function op a b = matches1?0, matches1 != [] = matches2?0, is_class b && matches2 != [] = matches3?0, op.symmetric && matches3 != [] = matches4?0, op.symmetric && is_class b && matches4 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (a.oo_binary_table op b); matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b); matches4 = oo_method_lookup (b.oo_binary_table op a); } /* A binary op: a is not a class, b is a class ... eg. "subtract" a b only one way to find a method: - b.oo_binary_search "subtract'" (converse (-)) a if this fails but op is a symmetric operator (eg. a + b == b + a), we can try reversing the args - b.oo_binary_search "add" (+) a, is_class b if that fails as well, but this is ==, do pointer equals as a fallback */ oo_binary'_function op a b = matches1?0, matches1 != [] = matches2?0, op.symmetric && matches2 != [] = pointer_equal a b, op.op_name == "equal" || op.op_name == "equal'" = not_pointer_equal a b, op.op_name == "not_equal" || op.op_name == "not_equal'" = error (_ "No method found for binary operator." ++ "\n" ++ _ "left" ++ " = " ++ print a ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "right" ++ " = " ++ print b) { matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a); matches2 = oo_method_lookup (b.oo_binary_table op a); } oo_unary_function op x = matches?0, matches != [] = error (_ "No method found for unary operator." ++ "\n" ++ _ "operator" ++ " = " ++ op.op_name ++ "\n" ++ _ "argument" ++ " = " ++ print x) { matches = oo_method_lookup (x.oo_unary_table op); } /* Base class for nip's built-in classes ... base check function, base * operator overload functions. */ _Object = class { check = check_args this; // these should always be defined _check_args = []; _check_all = []; /* Operator overloading stuff. */ oo_binary op x = oo_binary_function (oo_binary_lookup op) this x; oo_binary' op x = oo_binary'_function (oo_binary_lookup op) x this; oo_unary op = oo_unary_function (oo_unary_lookup op) this; oo_binary_table op x = []; oo_unary_table op = []; } ================================================ FILE: share/nip2/start/_convert.def ================================================ /* Try to make a Matrix ... works for Vector/Image/Real, plus image/real */ to_matrix x = to_matrix x.expr, is_Expression x = x, is_Matrix x = oo_unary_function to_matrix_op x, is_class x = tom x { to_matrix_op = Operator "to_matrix" tom Operator_type.COMPOUND false; tom x = Matrix (itom x), is_image x = Matrix [[x]], is_real x = Matrix [x], is_real_list x = Matrix x, is_matrix x = error (_ "bad arguments to " ++ "to_matrix"); itom i = (im_vips2mask ((double) i)).value, is_image i = error (_ "not image"); } /* Try to make a Vector ... works for Vector/Image/Real, plus image/real */ to_vector x = to_vector x.expr, is_Expression x = x, is_Vector x = oo_unary_function to_vector_op x, is_class x = tov x { to_vector_op = Operator "to_vector" tov Operator_type.COMPOUND false; tov x = Vector (itov x), is_image x = Vector [x], is_real x = Vector x, is_real_list x = Vector x?0, is_matrix x && len x == 1 = Vector (transpose x)?0, is_matrix x && len x?0 == 1 = error (_ "bad arguments to " ++ "to_vector"); itov i = v, is_image i = error (_ "not image") { m = im_vips2mask ((double) i); v = m.value?0, m.height == 1 = (transpose m.value)?0, m.width == 1 = error (_ "image is not 1xN or Nx1"); } } /* Try to make an Image ... works for Vector/Matrix/Real, plus image/real * Special case for Colour ... pull out the colour_space and set Type in the * image. */ to_image x = to_image x.expr, is_Expression x = Image x.value, is_Plot x = x, is_Image x = Image (image_set_type (Image_type.colour_spaces.lookup 0 1 x.colour_space) (mtoi [x.value])), is_Colour x = oo_unary_function to_image_op x, is_class x = toi x { to_image_op = Operator "to_image" toi Operator_type.COMPOUND false; toi x = Image x, is_image x = Image (mtoi [[x]]), is_real x = Image (mtoi [x]), is_real_list x = Image (mtoi x), is_matrix x = error (_ "bad arguments to " ++ "to_image"); // [[real]] -> image mtoi m = im_mask2vips (Matrix m), width != 3 = joinup (im_mask2vips (Matrix m)) { width = len m?0; height = len m; joinup i = b1 ++ b2 ++ b3 { b1 = extract_area 0 0 1 height i; b2 = extract_area 1 0 1 height i; b3 = extract_area 2 0 1 height i; } } } // like to_image, but we do 1x1 pixel + x, then embed it up // always make an unwrapped image for speed ... this gets used by ifthenelse // and stuff like that // format can be NULL, meaning set format from x to_image_size width height bands format x = x, is_image x = x.value, is_Image x = im'' { // we want x to set the target format if we don't have one, so we // can't use image_new im = im_black 1 1 bands + x; im' = clip2fmt format im, format != NULL = im; im'' = embed 1 0 0 width height im'; } /* Try to make a Colour. */ to_colour x = to_colour x.expr, is_Expression x = x, is_Colour x = to_colour (extract_area x.left x.top 1 1 x.image), is_Mark x = oo_unary_function to_colour_op x, is_class x = toc x { to_colour_op = Operator "to_colour" toc Operator_type.COMPOUND false; toc x = Colour (colour_space (get_type x)) (map mean (bandsplit (get_image x))), has_image x && has_type x = Colour "sRGB" [x, x, x], is_real x // since Colour can't do mono = Colour "sRGB" x, is_real_list x && is_list_len 3 x = map toc x, is_matrix x = error (_ "bad arguments to " ++ "to_colour"); colour_space type = table.get_name type, table.has_name type = error (_ "unable to make Colour from " ++ table.get_name type ++ _ " image") { table = Image_type.colour_spaces; } } /* Try to make a real. (not a Real!) */ to_real x = to_real x.expr, is_Expression x = oo_unary_function to_real_op x, is_class x = tor x { to_real_op = Operator "to_real" tor Operator_type.COMPOUND false; tor x = x, is_real x = abs x, is_complex x = 1, is_bool x && x = 0, is_bool x && !x = error (_ "bad arguments to " ++ "to_real"); } to_int x = (int) (to_real x); /* Try to make a list ... ungroup, basically. We remove the innermost layer of * Groups. */ to_list x = x.value, is_Group x && !contains_Group x.value = Group (map to_list x.value), is_Group x = x; /* Try to make a group. The outermost list objects become Group()'d. */ to_group x = Group x, is_list x = Group (map to_group x.value), is_Group x = x; /* Parse a positive integer. */ parse_pint l = foldl acc 0 l { acc sofar ch = sofar * 10 + parse_c ch; /* Turn a char digit to a number. */ parse_c ch = error (_ "not a digit"), !is_digit ch = (int) ch - (int) '0'; } /* Parse an integer, with an optional sign character. */ parse_int l = error (_ "badly formed number"), !is_list_len 2 parts = sign * n { parts = splitpl [member "+-", is_digit] l; n = parse_pint parts?1; sign = 1, parts?0 == [] || parts?0 == "+" = -1; } /* Parse a float. * [+-]?[0-9]*([.][0-9]*)?(e[0-9]+)? */ parse_float l = err, !is_list_len 4 parts = sign * (abs ipart + fpart) * 10 ** exp { err = error (_ "badly formed number"); parts = splitpl [ member "+-0123456789", member ".0123456789", member "eE", member "+-0123456789" ] l; ipart = parse_int parts?0; sign = 1, ipart >= 0 = -1; fpart = 0, parts?1 == []; = err, parts?1?0 != '.' = parse_pint (tl parts?1) / 10 ** (len parts?1 - 1); exp = 0, parts?2 == [] && parts?3 == [] = err, parts?2 == [] = parse_int parts?3; } /* Parse a time in "hh:mm:ss" into seconds. We could do this in one line :) = (sum @ map2 multiply (iterate (multiply 60) 1) @ reverse @ map parse_pint @ map (subscript (splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l))) [0,2,4]; but it's totally unreadable. */ parse_time l = error (_ "badly formed time"), !is_list_len 5 parts = s + 60 * m + 60 * 60 * h { parts = splitpl [is_digit, equal ':', is_digit, equal ':', is_digit] l; h = parse_int parts?0; m = parse_int parts?2; s = parse_int parts?4; } /* matrix to convert D65 XYZ to D50 XYZ ... direct conversion, found by * measuring a macbeth chart in D50 and D65 and doing a LMS to get a matrix */ D652D50_direct = Matrix [[ 1.13529, -0.0604663, -0.0606321 ], [ 0.0975399, 0.935024, -0.0256156 ], [ -0.0336428, 0.0414702, 0.994135 ]]; D502D65_direct = D652D50_direct ** -1; /* Convert normalised XYZ to bradford RGB. */ XYZ2RGBbrad = Matrix [[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]; /* Convert bradford RGB to normalised XYZ. */ RGBbrad2XYZ = XYZ2RGBbrad ** -1; D93_whitepoint = Vector [89.7400, 100, 130.7700]; D75_whitepoint = Vector [94.9682, 100, 122.5710]; D65_whitepoint = Vector [95.0470, 100, 108.8827]; D55_whitepoint = Vector [95.6831, 100, 92.0871]; D50_whitepoint = Vector [96.4250, 100, 82.4680]; A_whitepoint = Vector [109.8503, 100, 35.5849]; // 2856K B_whitepoint = Vector [99.0720, 100, 85.2230]; // 4874K C_whitepoint = Vector [98.0700, 100, 118.2300]; // 6774K E_whitepoint = Vector [100, 100, 100]; // ill. free D3250_whitepoint = Vector [105.6590, 100, 45.8501]; Whitepoints = Enum [ $D93 => D93_whitepoint, $D75 => D75_whitepoint, $D65 => D65_whitepoint, $D55 => D55_whitepoint, $D50 => D50_whitepoint, $A => A_whitepoint, $B => B_whitepoint, $C => C_whitepoint, $E => E_whitepoint, $D3250 => D3250_whitepoint ]; /* Convert D50 XYZ to D65 using the bradford chromatic adaptation approx. */ im_D502D65 xyz = xyz''' { xyz' = xyz / D50_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb / Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; // back to D65 xyz''' = xyz'' * D65_whitepoint; } /* Convert D65 XYZ to D50 using the bradford approx. */ im_D652D50 xyz = xyz''' { xyz' = xyz / D65_whitepoint; rgb = recomb XYZ2RGBbrad xyz'; // move white in bradford RGB rgb' = rgb * Vector [0.94, 1.02, 1.33]; xyz'' = recomb RGBbrad2XYZ rgb'; xyz''' = xyz'' * D50_whitepoint; } /* Convert D50 XYZ to Lab. */ im_D50XYZ2Lab xyz = im_XYZ2Lab_temp xyz D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; im_D50Lab2XYZ lab = im_Lab2XYZ_temp lab D50_whitepoint.value?0 D50_whitepoint.value?1 D50_whitepoint.value?2; /* ... and mono conversions */ im_sRGB2mono in = (image_set_type Image_type.B_W @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_mono2sRGB in = image_set_type Image_type.sRGB (in ++ in ++ in); im_sRGB2Lab = im_XYZ2Lab @ im_sRGB2XYZ; im_Lab2sRGB = im_XYZ2sRGB @ im_Lab2XYZ; // from the 16 bit RGB and GREY formats im_1628 x = im_clip (x >> 8); im_162f x = x / 256; im_8216 x = (im_clip2us x) << 8; im_f216 x = im_clip2us (x * 256); im_RGB162GREY16 in = (image_set_type Image_type.GREY16 @ clip2fmt (get_header "BandFmt" in) @ recomb (Matrix [[.3, .6, .1]])) in; im_GREY162RGB16 in = image_set_type Image_type.RGB16 (in ++ in ++ in); /* The vips8 scRGB functions. */ im_sRGB2scRGB in = out { [out] = vips_call "sRGB2scRGB" [in] []; } im_scRGB2sRGB in = out { [out] = vips_call "scRGB2sRGB" [in] []; } im_scRGB2XYZ in = out { [out] = vips_call "scRGB2XYZ" [in] []; } im_XYZ2scRGB in = out { [out] = vips_call "XYZ2scRGB" [in] []; } /* apply a func to an image ... make it 1 or 3 bands, and reapply other bands * on the way out. Except if it's LABPACK. */ colour_apply fn x = fn x, b == 1 || b == 3 || c == Image_coding.LABPACK = x'' { b = get_bands x; c = get_coding x; first = extract_bands 0 3 x, b > 3 = extract_bands 0 1 x; tail = extract_bands 3 (b - 3) x, b > 3 = extract_bands 1 (b - 1) x; x' = fn first; x'' = x' ++ clip2fmt (get_format x') tail; } /* Any 1-ary colour op, applied to Vector/Image/Matrix or image */ colour_unary fn x = oo_unary_function colour_op x, is_class x = colour_apply fn x, is_image x = colour_apply fn [x], is_real x = error (_ "bad arguments to " ++ "colour_unary") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back colour_op = Operator "colour_unary" colour_object Operator_type.COMPOUND_REWRAP false; colour_object x = colour_real_list x, is_real_list x = map colour_real_list x, is_matrix x = colour_apply fn x, is_image x = error (_ "bad arguments to " ++ "colour_unary"); colour_real_list l = (to_matrix (fn (float) (to_image (Vector l)).value)).value?0; } /* Any symmetric 2-ary colour op, applied to Vector/Image/Matrix or image ... * name is op name for error messages etc. */ colour_binary name fn x y = oo_binary_function colour_op x y, is_class x = oo_binary'_function colour_op x y, is_class y = fn x y, is_image x && is_image y = error (_ "bad arguments to " ++ name) { colour_op = Operator name colour_object Operator_type.COMPOUND_REWRAP true; colour_object x y = fn x y, is_image x && is_image y = colour_real_list fn x y, is_real_list x && is_real_list y = map (colour_real_list fn x) y, is_real_list x && is_matrix y = map (colour_real_list (converse fn) y) x, is_matrix x && is_real_list y = map2 (colour_real_list fn) x y, is_matrix x && is_matrix y = error (_ "bad arguments to " ++ name); colour_real_list fn l1 l2 = (to_matrix (fn i1 i2)).value?0 { i1 = (float) (to_image (Vector l1)).value; i2 = (float) (to_image (Vector l2)).value; } } _colour_conversion_table = [ /* Lines are [space-from, space-to, conversion function]. Could do * this as a big array, but table lookup feels safer. */ [B_W, B_W, image_set_type B_W], [B_W, XYZ, im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, LAB, im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_mono2sRGB @ im_clip], [B_W, sRGB, im_mono2sRGB @ im_clip], [B_W, scRGB, im_sRGB2scRGB @ im_mono2sRGB @ im_clip], [B_W, RGB16, image_set_type RGB16 @ im_8216 @ im_mono2sRGB], [B_W, GREY16, image_set_type GREY16 @ im_8216], [B_W, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [B_W, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab @ im_mono2sRGB @ im_clip], [XYZ, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_clip2f], [XYZ, XYZ, image_set_type XYZ], [XYZ, YXY, im_XYZ2Yxy @ im_clip2f], [XYZ, LAB, im_XYZ2Lab @ im_clip2f], [XYZ, LCH, im_Lab2LCh @ im_XYZ2Lab], [XYZ, UCS, im_XYZ2UCS @ im_clip2f], [XYZ, RGB, im_XYZ2disp @ im_clip2f], [XYZ, sRGB, im_XYZ2sRGB @ im_clip2f], [XYZ, scRGB, im_XYZ2scRGB @ im_clip2f], [XYZ, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [XYZ, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_clip2f], [YXY, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, XYZ, im_Yxy2XYZ @ im_clip2f], [YXY, YXY, image_set_type YXY], [YXY, LAB, im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, UCS, im_XYZ2UCS @ im_Yxy2XYZ @ im_clip2f], [YXY, RGB, im_XYZ2disp @ im_Yxy2XYZ @ im_clip2f], [YXY, sRGB, im_XYZ2sRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, scRGB, im_XYZ2scRGB @ im_Yxy2XYZ @ im_clip2f], [YXY, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [YXY, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_XYZ2Lab @ im_Yxy2XYZ @ im_clip2f], [LAB, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_Lab2XYZ @ im_clip2f], [LAB, XYZ, im_Lab2XYZ @ im_clip2f], [LAB, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_clip2f], [LAB, LAB, image_set_type LAB @ im_clip2f], [LAB, LCH, im_Lab2LCh @ im_clip2f], [LAB, UCS, im_Lab2UCS @ im_clip2f], [LAB, RGB, im_Lab2disp @ im_clip2f], [LAB, sRGB, im_Lab2sRGB @ im_clip2f], [LAB, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_clip2f], [LAB, LABQ, im_Lab2LabQ @ im_clip2f], [LAB, LABS, im_Lab2LabS @ im_clip2f], [LCH, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, XYZ, im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LAB, im_LCh2Lab @ im_clip2f], [LCH, LCH, image_set_type LCH], [LCH, UCS, im_LCh2UCS @ im_clip2f], [LCH, RGB, im_Lab2disp @ im_LCh2Lab @ im_clip2f], [LCH, sRGB, im_Lab2sRGB @ im_LCh2Lab @ im_clip2f], [LCH, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LCh2Lab @ im_clip2f], [LCH, LABQ, im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [LCH, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_LCh2Lab @ im_clip2f], [UCS, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_UCS2XYZ @ im_clip2f], [UCS, XYZ, im_UCS2XYZ @ im_clip2f], [UCS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LAB, im_UCS2Lab @ im_clip2f], [UCS, LCH, im_UCS2LCh @ im_clip2f], [UCS, UCS, image_set_type UCS], [UCS, RGB, im_Lab2disp @ im_UCS2Lab @ im_clip2f], [UCS, sRGB, im_Lab2sRGB @ im_UCS2Lab @ im_clip2f], [UCS, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_UCS2Lab @ im_clip2f], [UCS, LABQ, im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [UCS, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_UCS2Lab @ im_clip2f], [RGB, B_W, im_sRGB2mono @ im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, XYZ, im_disp2XYZ @ im_clip], [RGB, YXY, im_XYZ2Yxy @ im_disp2XYZ @ im_clip], [RGB, LAB, im_disp2Lab @ im_clip], [RGB, LCH, im_Lab2LCh @ im_disp2Lab @ im_clip], [RGB, UCS, im_Lab2UCS @ im_disp2Lab @ im_clip], [RGB, RGB, image_set_type RGB], [RGB, sRGB, im_XYZ2sRGB @ im_disp2XYZ @ im_clip], [RGB, scRGB, im_XYZ2scRGB @ im_disp2XYZ @ im_clip], [RGB, RGB16, image_set_type RGB16 @ im_8216], [RGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [RGB, LABQ, im_Lab2LabQ @ im_disp2Lab @ im_clip], [RGB, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_disp2Lab @ im_clip], [sRGB, B_W, im_sRGB2mono], [sRGB, XYZ, im_sRGB2XYZ @ im_clip], [sRGB, YXY, im_XYZ2Yxy @ im_sRGB2XYZ @ im_clip], [sRGB, LAB, im_sRGB2Lab @ im_clip], [sRGB, LCH, im_Lab2LCh @ im_sRGB2Lab @ im_clip], [sRGB, UCS, im_XYZ2UCS @ im_sRGB2XYZ @ im_clip], [sRGB, RGB, im_XYZ2disp @ im_sRGB2XYZ @ im_clip], [sRGB, sRGB, image_set_type sRGB], [sRGB, scRGB, im_sRGB2scRGB @ im_clip], [sRGB, RGB16, image_set_type RGB16 @ im_8216], [sRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono], [sRGB, LABQ, im_Lab2LabQ @ im_sRGB2Lab @ im_clip], [sRGB, LABS, im_Lab2LabS @ im_sRGB2Lab @ im_clip], [scRGB, B_W, im_sRGB2mono @ im_scRGB2sRGB], [scRGB, XYZ, im_scRGB2XYZ], [scRGB, YXY, im_XYZ2Yxy @ im_scRGB2XYZ], [scRGB, LAB, im_XYZ2Lab @ im_scRGB2XYZ], [scRGB, LCH, im_Lab2LCh @ im_XYZ2Lab @ im_scRGB2XYZ], [scRGB, UCS, im_XYZ2UCS @ im_scRGB2XYZ], [scRGB, RGB, im_XYZ2disp @ im_scRGB2XYZ], [scRGB, sRGB, im_scRGB2sRGB], [scRGB, scRGB, image_set_type scRGB], [scRGB, RGB16, image_set_type RGB16 @ im_8216 @ im_scRGB2sRGB], [scRGB, GREY16, image_set_type GREY16 @ im_8216 @ im_sRGB2mono @ im_scRGB2sRGB], [scRGB, LABQ, im_Lab2LabQ @ im_XYZ2Lab @ im_scRGB2XYZ], [scRGB, LABS, im_Lab2LabS @ im_XYZ2Lab @ im_scRGB2XYZ], [RGB16, B_W, im_1628 @ im_sRGB2mono], [RGB16, RGB, image_set_type RGB @ im_1628], [RGB16, sRGB, image_set_type sRGB @ im_1628], [RGB16, scRGB, im_sRGB2scRGB], [RGB16, RGB16, image_set_type RGB16], [RGB16, GREY16, im_RGB162GREY16], [RGB16, LABS, im_LabQ2LabS @ im_Lab2LabQ @ im_sRGB2Lab], [GREY16, B_W, image_set_type B_W @ im_1628], [GREY16, RGB, im_mono2sRGB @ im_1628], [GREY16, sRGB, im_mono2sRGB @ im_1628], [GREY16, scRGB, im_sRGB2scRGB @ im_mono2sRGB], [GREY16, RGB16, im_GREY162RGB16], [GREY16, GREY16, image_set_type GREY16], [LABQ, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab], [LABQ, XYZ, im_Lab2XYZ @ im_LabQ2Lab], [LABQ, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LAB, im_LabQ2Lab], [LABQ, LCH, im_Lab2LCh @ im_LabQ2Lab], [LABQ, UCS, im_Lab2UCS @ im_LabQ2Lab], [LABQ, RGB, im_LabQ2disp], [LABQ, sRGB, im_Lab2sRGB @ im_LabQ2Lab], [LABQ, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LabQ2Lab], [LABQ, LABQ, image_set_type LABQ], [LABQ, LABS, im_LabQ2LabS], [LABS, B_W, im_sRGB2mono @ im_Lab2sRGB @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, XYZ, im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, YXY, im_XYZ2Yxy @ im_Lab2XYZ @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, LAB, im_LabS2Lab], [LABS, LCH, im_Lab2LCh @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, UCS, im_Lab2UCS @ im_LabQ2Lab @ im_LabS2LabQ @ im_clip2s], [LABS, RGB, im_LabQ2disp @ im_LabS2LabQ @ im_clip2s], [LABS, sRGB, im_XYZ2sRGB @ im_Lab2XYZ @ im_LabS2Lab @ im_clip2s], [LABS, scRGB, im_XYZ2scRGB @ im_Lab2XYZ @ im_LabS2Lab @ im_clip2s], [LABS, LABQ, im_LabS2LabQ @ im_clip2s], [LABS, LABS, image_set_type LABS] ] { /* From Image_type ... repeat here for brevity. Use same ordering as * in Colour menu for consistency. */ B_W = 1; XYZ = 12; YXY = 23; LAB = 13; LCH = 19; UCS = 18; RGB = 17; sRGB = 22; scRGB = 28; RGB16 = 25; GREY16 = 26; LABQ = 16; LABS = 21; } /* Transform between two colour spaces. */ colour_transform from to in = colour_unary _colour_conversion_table?i?2 in, i >= 0 = error (_ "unable to convert " ++ Image_type.type_names.get_name from ++ _ " to " ++ Image_type.type_names.get_name to) { match x = x?0 == from && x?1 == to; i = index match _colour_conversion_table; } /* Transform to a colour space, assuming the type field in the input is * correct */ colour_transform_to to in = colour_transform (get_type in) to in; /* String for path separator on this platform. */ path_separator = expand "$SEP"; /* Form a relative pathname. * path_relative ["home", "john"] == "home/john" * path_relative [] == "" */ path_relative l = join_sep path_separator l; /* Form an absolute pathname. * path_absolute ["home", "john"] == "/home/john" * path_absolute [] == "/" * If the first component looks like 'A:', don't add an initial separator. */ path_absolute l = path_relative l, len l?0 > 1 && is_letter l?0?0 && l?0?1 == ':' = path_separator ++ path_relative l; /* Parse a pathname. * path_parse "/home/john" == ["home", "john"] * path_parse "home/john" == ["home", "john"] */ path_parse str = split (equal path_separator?0) str; /* Return $PATH, reformatted as [["comp1", "comp2"], ["comp1", "comp2"] ..] */ system_search_path = [vipsbin] ++ map path_parse (split (equal path_sep) (expand "$PATH")) { /* On some platforms we ship vips with a few extra progs. Search * $VIPSHOME/bin first. */ vipsbin = path_parse (expand "$VIPSHOME") ++ ["bin"]; path_sep = ':', expand "$SEP" == "/" = ';'; } /* Search $PATH for the first occurence of name, or "". */ search_for name = hits?0, hits != [] = "" { exe_name = name ++ expand "$EXEEXT"; form_path p = path_absolute (p ++ [exe_name]); paths = map form_path system_search_path; hits = dropwhile (equal []) (map search paths); } /* Search $PATH for the first occurence of name, error on failure. */ search_for_error name = path, path != "" = error (exe_name ++ " not found on your search path. " ++ "Check you have installed the program and it is on your PATH.") { exe_name = name ++ expand "$EXEEXT"; path = search_for name; } ================================================ FILE: share/nip2/start/_generate.def ================================================ /* make an image of size x by y whose pixels are their coordinates. */ make_xy x y = im_make_xy (to_real x) (to_real y); /* make an image with the specified properties ... pixel is (eg.) * Vector [0, 0, 0], or 12. If coding == labq, we ignore bands, format and * type, generate a 3 band float image, and lab2labq it before handing it * back. */ image_new w h b fmt coding type pixel xoff yoff = embed 1 0 0 w h im'''' { b' = 3, coding == Image_coding.LABPACK = b; fmt' = Image_format.FLOAT, coding == Image_coding.LABPACK = fmt; type' = Image_type.LAB, coding == Image_coding.LABPACK = type; im = im_black 1 1 (to_real b') + pixel; im' = clip2fmt fmt' im; im'' = im_Lab2LabQ im', coding == Image_coding.LABPACK; = im'; im''' = image_set_type type' im''; im'''' = image_set_origin xoff yoff im'''; } mkim options x y b = Image (image_new x y b (opt $format) (opt $coding) (opt $type) (opt $pixel) (opt $xoffset) (opt $yoffset)) { opt = get_option options [ $format => Image_format.UCHAR, $coding => Image_coding.NOCODING, $type => Image_type.sRGB, $pixel => 0, $xoffset => 0, $yoffset => 0 ]; } /* generate a slice of LAB space size x size pixels for L* == l */ lab_slice size l = image_set_type Image_type.LAB im { L = image_new size size 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W l 0 0; A1 = im_fgrey (to_real size) (to_real size); /* im_fgrey always makes 0-1, so these ranges can be wired in. */ A2 = A1 * 256 - 128; A4 = im_rot90 A2; im = image_set_origin (size / 2) (size / 2) (L ++ A2 ++ A4); } /* Look at Image, try to make a Colour (failing that, a Vector) which is white * for that image type. */ image_white im = colour_transform_to type white_lab, bands == 3 && coding == Image_coding.NOCODING && colour_spaces.present 1 type = white_lab, coding == Image_coding.LABPACK = Vector (replicate bands (max_value.lookup 1 0 format)) { bands = im.bands; type = im.type; format = im.format; coding = im.coding; colour_spaces = Image_type.colour_spaces; // white as LAB white_lab = Colour "Lab" [100, 0, 0]; // maximum value for this numeric type max_value = Table [ [255, Image_format.DPCOMPLEX], [255, Image_format.DOUBLE], [255, Image_format.COMPLEX], [255, Image_format.FLOAT], [2 ** 31 - 1, Image_format.INT], [2 ** 32 - 1, Image_format.UINT], [2 ** 15 - 1, Image_format.SHORT], [2 ** 16 - 1, Image_format.USHORT], [2 ** 7 - 1, Image_format.CHAR], [2 ** 8 - 1, Image_format.UCHAR] ]; } /* Make a seperable gaussian mask. */ matrix_gaussian_blur radius = im_gauss_imask_sep (radius / 3) 0.2; /* Make a seperable square mask. */ matrix_blur radius = Matrix_con (sum mask_sq_line) 0 [mask_sq_line] { mask_sq_line = replicate (2 * radius - 1) 1; } /* Make a colour from a temperature. */ colour_from_temp T = error (_ "T out of range"), T < 1667 || T > 25000 = Colour "Yxy" [50, x, y] { // Kim et all approximation // see eg. http://en.wikipedia.org/wiki/Planckian_locus#Approximation x = -0.2661239 * 10 ** 9 / T ** 3 - 0.2343580 * 10 ** 6 / T ** 2 + 0.8776956 * 10 ** 3 / T + 0.179910, T < 4000 = -3.0258469 * 10 ** 9 / T ** 3 + 2.1070379 * 10 ** 6 / T ** 2 + 0.2226347 * 10 ** 3 / T + 0.240390; y = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219638, T < 2222 = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, T < 4000 = 3.0817580 * x ** 3 - 5.87338670 * x ** 2 + 3.75112997 * x - 0.37001483; } temp_from_colour z = T { c = colour_transform_to Image_type.YXY (to_colour z); x = c.value?1; y = c.value?2; // McCamy's approximation, see eg. // http://en.wikipedia.org/wiki/Color_temperature#Approximation xe = 0.332; ye = 0.1858; n = (x - xe) / (y - ye); T = -449 * n ** 3 + 3525 * n ** 2 - 6823.3 * n + 5520.33; } ================================================ FILE: share/nip2/start/_joe_extra.def ================================================ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Frame_item = class Menupullright "Picture _Frame" "working with images of frames" { //////////////////////////////////////////////////////////////////////////////////// Build_frame_item = class Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b" { //////////////////////////////////////////////////////////////////////////////////// Frame_corner_item = class Menuaction "_Frame Corner" "copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Simple_frame_item = class Menuaction "_Simple Frame" "extends or shortens the central sections of a simple frame, a, to fit round a given image, b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 0; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; //Scale frame image if required. _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.mount_colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } //////////////////////////////////////////////////////////////////////////////////// Complex_frame_item = class Menuaction "_Complex Frame" "extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" { prefs = Workspaces.Preferences; action a b = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _check_all = [ [a.coding == b.coding && a.bands == b.bands, "a.coding == b.coding && a.bands == b.bands"] ]; _vislevel = 3; ppcm = Expression "Number of pixels per cm" 25; /* Given the value of ppcm, distance between the inner edge of the frame * and the outer edge of the image. +ve values mean the frame overlaps * the image. */ overlap = Expression "Size of frame overlap in cm" 0; variables = Frame_variables 1; _type = Image_type.colour_spaces.get_name b.type; //If applied the count colour be seen for -ve values of overlap mount_options = Mount_options _type ppcm.expr; _cs = variables.corner_section.value; _es = variables.edge_section.value; _ms = variables.middle_section.value; _ov = ppcm.expr * overlap.expr; _sf = variables.scale_factor.expr; _bf = variables.blend_fraction.value; _a = a, _sf == 1; = a, _sf == 0; = Image (resize Kernel_linear _sf _sf a.value); _im_w = b.width; _im_h = b.height + mount_options._los, mount_options.apply = b.height; _os = mount_options._los, mount_options.apply = 0; _cl = Vector mount_options.colour.value, mount_options.apply = 0; //Produce scaled and resized frame. frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option; //Resize image canvas and applied mount colour as required. _pos_im = frame_position_image b frame _os _cl; //Wrap frame round image. _result = if (frame == 0) then _pos_im else frame; } } } //////////////////////////////////////////////////////////////////////////////////// Straighten_frame_item = class Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" { action a = Perspective_item.action a; } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Select_item = class Menupullright "_Select" "select user defined areas of an image" { prefs = Workspaces.Preferences; /* Option toggle used to define whether the user is replacing a * dark or a light area. */ _control = Option "Make" [ "Selection Brighter", "Selection Darker", "Selection Black", "Selection White", "Background Black", "Background White", "Mask" ] 4; control_selection mask im no = [ if mask then im * 1.2 else im * 1, if mask then im * 0.8 else im * 1, if mask then 0 else im, if mask then 255 else im, if mask then im else 0, if mask then im else 255, mask ]?no; Rectangle = class Menuaction "_Rectangle" "use an Arrow or Region x to define a rectangle" { action x = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { im = x.image; mask = Image m { rx = x.region_rect, is_Region x = x; b = image_new im.width im.height 1 0 0 1 0 0 0; w = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0; m = insert_noexpand rx.nleft rx.ntop w b; } } } } Elipse = class Menuaction "_Ellipse" "use a line/arrow x to define the center point radius and direction of an ellipse" { action x = class _result { _vislevel = 3; control = _control; width = Scale "Width" 0.01 1 0.5; _result = control_selection mask im control { mask = select_ellipse x width.value; im = x.image; } } } Tetragon = class Menuaction "_Tetragon" "selects the convex area defined by four points" { action a b c d = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_tetragon a b c d; im = get_image a; } } } Polygon = class Menuaction "_Polygon" "selects a polygon from an ordered group of points" { action pt_list = class _result { _vislevel = 3; control = _control; _result = control_selection mask im control { mask = select_polygon pt_list; im = get_image ((pt_list.value)?0); } } } sep1 = Menuseparator; Threshold_item = class Menuaction "Thres_hold" "simple image threshold" { action x = class _result { _vislevel = 3; t = Scale "Threshold" 0 mx (mx / 2) { mx = Image_format.maxval x.format, is_Image x = 255; } _result = map_unary (more t.value) x; } } Threshold_percent_item = class Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" { action x = class _result { _vislevel = 3; t = Scale "Percentage of pixels" 0 100 50; _result = map_unary (more (hist_thresh (t.value / 100) x)) x; } } sep2 = Menuseparator; Segment_item = class Menuaction "_Segment" "break image into disjoint regions" { action x = class _result { _vislevel = 3; segments = Expression "Number of disjoint regions" (map_unary (get_header "n-segments") _result); _result = map_unary segment x; } } Fill_item = class Menuaction "_Fill" "fill zero pixels with the nearest non-zero" { action x = class Image _result { _vislevel = 3; distance = Image _distance; [_result, _distance] = vips_call "fill_nearest" [x.value] [ "distance" => true ]; } } fill_nearest x = oo_unary_function nearest_op x, is_class x = near x, is_image x = error (_ "bad arguments to " ++ "fill_nearest") { nearest_op = Operator "fill_nearest" fill_nearest Operator_type.COMPOUND_REWRAP false; near x = [out, distance] { [out, distance] = vips_call "fill_nearest" [x] [ "distance" => true ]; } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_match_item = class Menuaction "_Perspective Match" "rotate, scale and skew one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.1 0.9; ap4 = Mark_relative _a 0.9 0.9; bp1 = Mark_relative _b 0.1 0.1; bp2 = Mark_relative _b 0.9 0.1; bp3 = Mark_relative _b 0.1 0.9; bp4 = Mark_relative _b 0.9 0.9; _result = map_binary process x y { f1 = _a.width / _b.width; f2 = _a.height / _b.height; rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; pl = sort_pts_clockwise [bp1, bp2, bp3, bp4]; to = [ rl?0.left, rl?0.top, rl?1.left, rl?1.top, rl?2.left, rl?2.top, rl?3.left, rl?3.top ]; from = [ pl?0.left * f1, pl?0.top * f2, pl?1.left * f1, pl?1.top * f2, pl?2.left * f1, pl?2.top * f2, pl?3.left * f1, pl?3.top * f2 ]; trans = perspective_transform to from; process a b = transform 1 0 trans b2 { b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1) = resize Kernel_linear f1 1 b1 {b1 = resize Kernel_linear 1 f2 b;} } } } }; //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Perspective_item = class Menuaction "Pe_rspective Distort" "rotate, scale and skew an image with respect to defined points" { action x = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1; ap1 = Mark_relative _a 0.1 0.1; ap2 = Mark_relative _a 0.9 0.1; ap3 = Mark_relative _a 0.9 0.9; ap4 = Mark_relative _a 0.1 0.9; _result = map_unary process x { trans = [perspective_transform to from, perspective_transform from to]?(dir.value) { rl = sort_pts_clockwise [ap1, ap2, ap3, ap4]; to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top, (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top]; from=[0, 0, (_a.width - 1), 0, (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)]; } process a = transform 1 0 trans a; } } }; ================================================ FILE: share/nip2/start/_joe_utilities.def ================================================ /* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if mask then im else 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = get_header "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = get_header "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; }; ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = get_header "Xsize" (get_image im), dir == 0 = get_header "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); }; ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = Image (get_image line); //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_Group pt_list; pt_l = pt_list.value, group_check = pt_list; im = Image (get_image (pt_l?0)); im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; }; ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; }; ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }; Mount_options _ctype _ppcm = class { _vislevel = 3; apply = Toggle "Apply mount options" false; ls = Expression "Lower mount section bigger by (cm)" 0; mount_colour = Colour _ctype [0, 0, 0]; _los = ls.expr * _ppcm; }; Frame_variables comp = class { _vislevel = 3; scale_factor = Expression "scale the size of the frame by" 1; /* These sliders define the fraction of the frames width or height is extracted * to produce each of the particular regions. */ corner_section = Scale "Corner section" 0.1 1 0.5; edge_section = Scale "Edge section" 0.1 1 0.2, comp > 0 = "Only required for complex frames"; middle_section = Scale "Middle section" 0.1 1 0.2; blend_fraction = Scale "Blend fraction" 0.1 0.9 0.1; option = Toggle "Use mirror of left-side to make right" true; }; ================================================ FILE: share/nip2/start/_list.def ================================================ /* any l: or all the elements of list l together * * any (map (equal 0) list) == true, if any element of list is zero. * any :: [bool] -> bool */ any = foldr logical_or false; /* all l: and all the elements of list l together * * all (map (==0) list) == true, if every element of list is zero. * all :: [bool] -> bool */ all = foldr logical_and true; /* concat l: join a list of lists together * * concat ["abc","def"] == "abcdef". * concat :: [[*]] -> [*] */ concat l = foldr join [] l; /* delete eq x l: delete the first x from l * * delete equal 'b' "abcdb" == "acdb" * delete :: (* -> bool) -> * -> [*] -> [*] */ delete eq a l = [], l == [] = y, eq a b = b : delete eq a y { b:y = l; } /* difference eq a b: delete b from a * * difference equal "asdf" "ad" == "sf" * difference :: (* -> bool) -> [*] -> [*] -> [*] */ difference = foldl @ converse @ delete; /* drop n l: drop the first n elements from list l * * drop 3 "abcd" == "d" * drop :: num -> [*] -> [*] */ drop n l = l, n <= 0 || l == [] = drop (n - 1) (tl l); /* dropwhile fn l: drop while fn is true * * dropwhile is_digit "1234pigs" == "pigs" * dropwhile :: (* -> bool) -> [*] -> [*] */ dropwhile fn l = [], l == [] = dropwhile fn x, fn a = l { a:x = l; } /* extract n l: extract element at index n from list l */ extract = converse subscript; /* filter fn l: return all elements of l for which predicate fn holds * * filter is_digit "1one2two3three" = "123" * filter :: (* -> bool) -> [*] -> [*] */ filter fn l = foldr addif [] l { addif x l = x : l, fn x; = l; } /* flatten x: flatten a list of lists of things into a simple list * * flatten :: [[*]] -> [*] */ flatten x = foldr flat [] x, is_list x = x { flat x sofar = foldr flat sofar x, is_list x = x : sofar; } /* foldl fn st l: fold list l from the left with function fn and start st * * Start from the left hand end of the list (unlike foldr, see below). * foldl is less useful (and much slower). * * foldl fn start [a,b .. z] = ((((st fn a) fn b) ..) fn z) * foldl :: (* -> ** -> *) -> * -> [**] -> * */ foldl fn st l = st, l == [] = foldl fn (fn st x) xs { x:xs = l; } /* foldl1 fn l: like foldl, but use the 1st element as the start value * * foldl1 fn [1,2,3] == ((1 fn 2) fn 3) * foldl1 :: (* -> * -> *) -> [*] -> * */ foldl1 fn l = [], l == [] = foldl fn x xs { x:xs = l; } /* foldr fn st l: fold list l from the right with function fn and start st * * foldr fn st [a,b..z] = (a fn (b fn (.. (z fn st)))) * foldr :: (* -> ** -> **) -> ** -> [*] -> ** */ foldr fn st l = st, l == [] = fn x (foldr fn st xs) { x:xs = l; } /* foldr1 fn l: like foldr, but use the last element as the start value * * foldr1 fn [1,2,3,4] == (1 fn (2 fn (3 fn 4))) * foldr1 :: (* -> * -> *) -> [*] -> * */ foldr1 fn l = [], l == [] = x, xs == [] = fn x (foldr1 fn xs) { x:xs = l; } /* Search a list for an element, returning its index (or -1) * * index (equal 12) [13,12,11] == 1 * index :: (* -> bool) -> [*] -> real */ index fn list = search list 0 { search l n = -1, l == [] = n, fn x = search xs (n + 1) { x:xs = l; } } /* init l: remove last element of list l * * The dual of tl. * init [1,2,3] == [1,2] * init :: [*] -> [*] */ init l = error "init of []", l == []; = [], tl l == []; = x : init xs { x:xs = l; } /* iterate f x: repeatedly apply f to x * * return the infinite list [x, f x, f (f x), ..]. * iterate (multiply 2) 1 == [1, 2, 4, 8, 16, 32, 64 ... ] * iterate :: (* -> *) -> * -> [*] */ iterate f x = x : iterate f (f x); /* join_sep sep l: join a list with a separator * * join_sep ", " (map print [1 .. 4]) == "1, 2, 3, 4" * join_sep :: [*] -> [[*]] -> [*] */ join_sep sep l = foldl1 fn l { fn a b = a ++ sep ++ b; } /* last l: return the last element of list l * * The dual of hd. last [1,2,3] == 3 * last :: [*] -> [*] */ last l = error "last of []", l == [] = x, xs == [] = last xs { x:xs = l; } /* len l: length of list l * (see also is_list_len and friends in predicate.def) * * len :: [*] -> num */ len l = 0, l == [] = 1 + len (tl l); /* limit l: return the first element of l which is equal to its predecessor * * useful for checking for convergence * limit :: [*] -> * */ limit l = error "incorrect use of limit", l == [] || tl l == [] || tl (tl l) == [] = a, a == b = limit (b : x) { a:b:x = l; } /* Turn a function of n args into a function which takes a single arg of an * n-element list. */ list_1ary fn x = fn x?0; list_2ary fn x = fn x?0 x?1; list_3ary fn x = fn x?0 x?1 x?2; list_4ary fn x = fn x?0 x?1 x?2 x?3; list_5ary fn x = fn x?0 x?1 x?2 x?3 x?4; list_6ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5; list_7ary fn x = fn x?0 x?1 x?2 x?3 x?4 x?5 x?6; /* map fn l: map function fn over list l * * map :: (* -> **) -> [*] -> [**] */ map f l = [], l == []; = f (hd l) : map f (tl l); /* map2 fn l1 l2: map two lists together with fn * * map2 :: (* -> ** -> ***) -> [*] -> [**] -> [***] */ map2 fn l1 l2 = map (list_2ary fn) (zip2 l1 l2); /* map3 fn l1 l2 l3: map three lists together with fn * * map3 :: (* -> ** -> *** -> ****) -> [*] -> [**] -> [***] -> [****] */ map3 fn l1 l2 l3 = map (list_3ary fn) (zip3 l1 l2 l3); /* member l x: true if x is a member of list l * * is_digit == member "0123456789" * member :: [*] -> * -> bool */ member l x = any (map (equal x) l); /* merge b l r: merge two lists based on a bool list * * merge :: [bool] -> [*] -> [*] -> [*] */ merge p l r = [], p == [] || l == [] || r == [] = a : merge z x y, c = b : merge z x y { a:x = l; b:y = r; c:z = p; } /* mkset eq l: remove duplicates from list l using equality function * * mkset :: (* -> bool) -> [*] -> [*] */ mkset eq l = [], l == [] = a : filter (not @ eq a) (mkset eq x) { a:x = l; } /* postfix l r: add r to the end of list l * * The dual of ':'. * postfix :: [*] -> ** -> [*,**] */ postfix l r = l ++ [r]; /* repeat x: make an infinite list of xes * * repeat :: * -> [*] */ repeat x = map (const x) [1..]; /* replicate n x: make n copies of x in a list * * replicate :: num -> * -> [*] */ replicate n x = take n (repeat x); /* reverse l: reverse list l * * reverse :: [*] -> [*] */ reverse l = foldl (converse cons) [] l; /* scanl fn st l: apply (foldl fn r) to every initial segment of a list * * scanl add 0 [1,2,3] == [1,3,6] * scanl :: (* -> ** -> *) -> * -> [**] -> [*] */ scanl fn st l = st, l == [] = st' : scanl fn st' xs { x:xs = l; st' = fn st x; } /* sort l: sort list l into ascending order * * sort :: [*] -> [*] */ sort l = sortc less_equal l; /* sortc comp l: sort list l into order using a comparision function * * Uses merge sort (n log n behaviour) * sortc :: (* -> * -> bool) -> [*] -> [*] */ sortc comp l = l, n <= 1 = merge (sortc comp (take n2 l)) (sortc comp (drop n2 l)) { n = len l; n2 = (int) (n / 2); /* merge l1 l2: merge sorted lists l1 and l2 to make a single * sorted list */ merge l1 l2 = l2, l1 == [] = l1, l2 == [] = a : merge x (b : y), comp a b = b : merge (a : x) y { a:x = l1; b:y = l2; } } /* sortpl pl l: sort by a list of predicates * * sortpl :: (* -> bool) -> [*] -> [*] */ sortpl pl l = sortc (test pl) l { /* Comparision function ... put true before false, if equal move on to * the next predicate. */ test pl a b = true, pl == [] = ta, ta != tb = test (tl pl) a b { ta = pl?0 a; tb = pl?0 b; } } /* sortr l: sort list l into descending order * * sortr :: [*] -> [*] */ sortr l = sortc more l; /* split fn l: break a list into sections separated by many fn * * split is_space " hello world " == ["hello", "world"] * split is_space " " == [] * split :: (* -> bool) -> [*] -> [[*]] */ split fn l = [], l == [] || l' == [] = head : split fn tail { nfn = not @ fn; l' = dropwhile fn l; head = takewhile nfn l'; tail = dropwhile nfn l'; } /* splits fn l: break a list into sections separated by a single fn * * split (equal ',') ",,1" == ["", "", "1"] * split :: (* -> bool) -> [*] -> [[*]] */ splits fn l = [], l == [] = head : splits fn tail { fn' = not @ fn; dropif x = [], x == [] = tl x; head = takewhile fn' l; tail = dropif (dropwhile fn' l); } /* splitpl fnl l: split a list up with a list of predicates * * splitpl [is_digit, is_letter, is_digit] "123cat" == ["123", "cat", []] * splitpl :: [* -> bool] -> [*] -> [[*]] */ splitpl fnl l = l, fnl == [] = head : splitpl (tl fnl) tail { head = takewhile (hd fnl) l; tail = dropwhile (hd fnl) l; } /* split_lines n l: split a list into equal length lines * * split_lines 4 "1234567" == ["1234", "567"] * splitl :: int -> [*] -> [[*]] */ split_lines n l = [], l == [] = take n l : split_lines n (drop n l); /* take n l: take the first n elements from list l * take :: num -> [*] -> [*] */ take n l = [], n <= 0 = [], l == [] = hd l : take (n-1) (tl l); /* takewhile fn l: take from the front of a list while predicate fn holds * * takewhile is_digit "123onetwothree" == "123" * takewhile :: (* -> bool) -> [*] -> [*] */ takewhile fn l = [], l == [] = hd l : takewhile fn (tl l), fn (hd l) = []; /* zip2 l1 l2: zip two lists together * * zip2 [1,2] ['a', 'b', 'c'] == [[1,'a'],[2,'b']] * zip2 :: [*] -> [**] -> [[*,**]] */ zip2 l1 l2 = [], l1 == [] || l2 == [] = [hd l1, hd l2] : zip2 (tl l1) (tl l2); /* zip3 l1 l2 l3: zip three lists together * * zip3 [1,2] ['a', 'b', 'c'] [true] == [[1,'a',true]] * zip3 :: [*] -> [**] ->[***] -> [[*,**,***]] */ zip3 l1 l2 l3 = [], l1 == [] || l2 == [] || l3 == [] = [hd l1, hd l2, hd l3] : zip3 (tl l1) (tl l2) (tl l3); ================================================ FILE: share/nip2/start/_magick.def ================================================ /* ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net). 1-Apr-2014 Minor corrections to Geometry_widget and Alpha. Added loads of widgets and Menuactions. Not fully tested. 5-Apr-2014 Many more menu actions. Reorganised Magick menu. 10-Apr-2014 Many more menu actions. 11-Apr-2014 jcupitt Split to separate _magick.def Add 0-ary and 2-ary system Put utility funcs into a Magick class 11-Apr-2014 snibgo Added VirtualPixelBack for cases where background is only relevant when VP=Background 17-Apr-2014 snibgo Many small changes. 2-May-2014 jcupitt Added Magick.version 30-June-2014 Put single-quotes around command exe to help win 1-July-2014 Automatically fall back to gm if we can't find convert 17-July-2014 better GM support Last update: 17-July-2014. For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc. */ /* Put these in a class to avoid filling the main namespace with IM stuff. */ Magick = class { // first gm on path, or "" gm_path = search_for "gm"; // first convert on $PATH, or "" // we check for the convert we ship first convert_path = vips_convert, vips_convert != "" = search_for "convert" { // the convert we ship with the vips binary on some platforms, or "" vips_convert = search (path_absolute convert) { vipshome = path_parse (expand "$VIPSHOME"); convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"]; } } use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK; // Are we in GM or IM mode? use_gm = true, use_gm_pref && gm_path != "" = false, !use_gm_pref && convert_path != "" = false, convert_path != "" = true, gm_path != "" = error "neither IM nor GM executable found"; command_path = gm_path, use_gm = convert_path; // try to get the version as eg. [6, 7, 7, 10] // GM versions are smaller, typically [1, 3, 18] version = map parse_int (split (member ".-") version_string) { [output] = vips_call "system" ["'" ++ command_path ++ "' -version"] [$log=>true]; version_string = (split (equal ' ') output)?1, use_gm = (split (equal ' ') output)?2; } // make a command-line ... args is a [str] we join with spaces command args = "'" ++ command_path ++ "' " ++ join_sep " " args' { args' = ["convert"] ++ args, use_gm = args; } // capabilities ... different versions support different features, we // turn features on and off based on these // would probably be better to test for caps somehow has_intensity = false, use_gm = version?0 > 6 || version?1 > 7; has_channel = false, use_gm = version?0 > 6 || version?1 > 7; system0 cmd = system_image0 cmd; system cmd x = map_unary (system_image cmd) x; system2 cmd x y = map_binary (system_image2 cmd) x y; system3 cmd x y z = map_trinary (system_image3 cmd) x y z; radius_widget = Scale "Radius" 0 100 10; sigma_widget = Scale "Sigma" 0.1 10 1; angle_widget = Scale "Angle (degrees)" (-360) 360 0; text_widget = String "Text to draw" "AaBbCcDdEe"; gamma_widget = Scale "Gamma" 0 10 1; colors_widget = Scale "Colors" 1 10 3; resize_widget = Scale "Resize (percent)" 0 500 100; fuzz_widget = Scale "Fuzz (percent)" 0 100 0; blur_rad_widget = Scale "Radius (0=auto)" 0 100 0; // a colour with no enclosing quotes ... use this if we know there are // some quotes at an outer level print_colour_nq triple = concat ["#", concat (map fmt triple)] { fmt x = reverse (take 2 (reverse (print_base 16 (x + 256)))); } // we need the quotes because # is the comment character in *nix print_colour triple = "\"" ++ print_colour_nq triple ++ "\""; Foreground triple = class Colour "sRGB" triple { _flag = "-fill " ++ print_colour triple; Colour_edit space triple = this.Foreground triple; } foreground_widget = Foreground [0, 0, 0]; GeneralCol triple = class Colour "sRGB" triple { _flag = print_colour_nq triple; Colour_edit space triple = this.GeneralCol triple; } generalcol_widget = GeneralCol [0, 0, 0]; Background triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" false; _flag = "-background " ++ if isNone then "None" else print_colour triple; Colour_edit space triple = this.Background triple; } background_widget = Background [255, 255, 255]; Bordercol triple = class Colour "sRGB" triple { _flag = "-bordercolor " ++ print_colour triple; Colour_edit space triple = this.Bordercol triple; } bordercol_widget = Bordercol [0, 0, 0]; Mattecol triple = class Colour "sRGB" triple { _flag = "-mattecolor " ++ print_colour triple; Colour_edit space triple = this.Mattecol triple; } mattecol_widget = Mattecol [189, 189, 189]; // FIXME: Undercolour, like many others, can have alpha channel. // How does user input this? With a slider? Undercol triple = class Colour "sRGB" triple { isNone = Toggle "None (transparent black)" true; _flag = if isNone then "" else ("-undercolor " ++ print_colour triple); Colour_edit space triple = this.Undercol triple; } undercol_widget = Undercol [0, 0, 0]; changeCol_widget = class { _vislevel = 3; colour = GeneralCol [0, 0, 0]; fuzz = fuzz_widget; nonMatch = Toggle "change non-matching colours" false; } Alpha alpha = class Option_string "Alpha" [ "On", "Off", "Set", "Opaque", "Transparent", "Extract", "Copy", "Shape", "Remove", "Background" ] alpha { _flag = "-alpha " ++ alpha; Option_edit caption labels value = this.Alpha labels?value; } alpha_widget = Alpha "On"; Antialias value = class Toggle "Antialias" value { _flag = "-antialias", value = "+antialias"; Toggle_edit caption value = this.Antialias value; } antialias_widget = Antialias true; Builtin builtin = class Option_string "Builtin" [ // See http://www.imagemagick.org/script/formats.php "rose:", "logo:", "wizard:", "granite:", "netscape:" ] builtin { _flag = builtin; Option_edit caption labels value = this.Builtin labels?value; } builtin_widget = Builtin "rose:"; channels_widget = class { // FIXME? Can we grey-out alpha when we have no alpha channel, // show CMY(K) instead of RGB(K) etc? // Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA. ChanR valueR = class Toggle "Red" valueR { _flag = "R", valueR = ""; Toggle_edit caption valueR = this.ChanR valueR; } channelR = ChanR true; ChanG valueG = class Toggle "Green" valueG { _flag = "G", valueG = ""; Toggle_edit caption valueG = this.ChanG valueG; } channelG = ChanG true; ChanB valueB = class Toggle "Blue" valueB { _flag = "B", valueB = ""; Toggle_edit caption valueB = this.ChanB valueB; } channelB = ChanB true; ChanK valueK = class Toggle "Black" valueK { _flag = "K", valueK = ""; Toggle_edit caption valueK = this.ChanK valueK; } channelK = ChanK true; ChanA valueA = class Toggle "Alpha" valueA { _flag = "A", valueA = ""; Toggle_edit caption valueA = this.ChanA valueA; } channelA = ChanA false; ChanSy valueSy = class Toggle "Sync" valueSy { _flag = ",sync", valueSy = ""; Toggle_edit caption valueSy = this.ChanSy valueSy; } channelSy = ChanSy true; _rgbka = concat [channelR._flag, channelG._flag, channelB._flag, channelK._flag, channelA._flag ]; _flag = "", _rgbka == "" || !has_channel = concat [ "-channel ", _rgbka, channelSy._flag ]; } ch_widget = channels_widget; Colorspace colsp = class Option_string "Colorspace" [ "CIELab", "CMY", "CMYK", "Gray", "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "Lab", "LCH", "LCHab", "LCHuv", "LMS", "Log", "Luv", "OHTA", "Rec601Luma", "Rec601YCbCr", "Rec709Luma", "Rec709YCbCr", "RGB", "scRGB", "sRGB", "Transparent", "XYZ", "YCbCr", "YDbDr", "YCC", "YIQ", "YPbPr", "YUV" ] colsp { _flag = colsp; Option_edit caption labels value = this.Colorspace labels?value; } colorspace_widget = Colorspace "sRGB"; Compose comp = class Option_string "Compose method" [ "Atop", "Blend", "Blur", "Bumpmap", "ChangeMask", "Clear", "ColorBurn", "ColorDodge", "Colorize", "CopyBlack", "CopyBlue", "CopyCyan", "CopyGreen", "Copy", "CopyMagenta", "CopyOpacity", "CopyRed", "CopyYellow", "Darken", "DarkenIntensity", "DivideDst", "DivideSrc", "Dst", "Difference", "Displace", "Dissolve", "Distort", "DstAtop", "DstIn", "DstOut", "DstOver", "Exclusion", "HardLight", "Hue", "In", "Lighten", "LightenIntensity", "LinearBurn", "LinearDodge", "LinearLight", "Luminize", "Mathematics", "MinusDst", "MinusSrc", "Modulate", "ModulusAdd", "ModulusSubtract", "Multiply", "None", "Out", "Overlay", "Over", "PegtopLight", "PinLight", "Plus", "Replace", "Saturate", "Screen", "SoftLight", "Src", "SrcAtop", "SrcIn", "SrcOut", "SrcOver", "VividLight", "Xor" ] comp { _flag = "-compose " ++ comp; Option_edit caption labels value = this.Compose labels?value; } compose_widget = Compose "Over"; // FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string. // FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack coordinate_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; _flag = concat [print x.expr, ",", print y.expr]; }; Distort distort = class Option_string "Distort" [ "Affine", "AffineProjection", "ScaleRotateTranslate", "SRT", "Perspective", "PerspectiveProjection", "BilinearForward", "BilinearReverse", "Polynomial", "Arc", "Polar", "DePolar", "Barrel", "BarrelInverse", "Shepards", "Resize" ] distort { _flag = distort; Option_edit caption labels value = this.Distort labels?value; } distort_widget = Distort "SRT"; Dither dither = class Option_string "Dither" [ "None", "FloydSteinberg", "Riemersma" ] dither { _flag = "-dither " ++ dither; Option_edit caption labels value = this.Dither labels?value; } dither_widget = Dither "FloydSteinberg"; Evaluate eval = class Option_string "Evaluate operation" [ "Abs", "Add", "AddModulus", "And", "Cos", "Cosine", "Divide", "Exp", "Exponential", "GaussianNoise", "ImpulseNoise", "LaplacianNoise", "LeftShift", "Log", "Max", "Mean", "Median", "Min", "MultiplicativeNoise", "Multiply", "Or", "PoissonNoise", "Pow", "RightShift", "Set", "Sin", "Sine", "Subtract", "Sum", "Threshold", "ThresholdBlack", "ThresholdWhite", "UniformNoise", "Xor" ] eval { _flag = "-evaluate " ++ eval; Option_edit caption labels value = this.Evaluate labels?value; } evaluate_widget = Evaluate "Add"; Filter filt = class Option_string "Filter" [ "default", "Bartlett", "Blackman", "Bohman", "Box", "Catrom", "Cosine", "Cubic", "Gaussian", "Hamming", "Hann", "Hermite", "Jinc", "Kaiser", "Lagrange", "Lanczos", "Lanczos2", "Lanczos2Sharp", "LanczosRadius", "LanczosSharp", "Mitchell", "Parzen", "Point", "Quadratic", "Robidoux", "RobidouxSharp", "Sinc", "SincFast", "Spline", "Triangle", "Welch" ] filt { _flag = if filt == "default" then "" else "-filter " ++ filt; Option_edit caption labels value = this.Filter labels?value; } filter_widget = Filter "default"; Function func = class Option_string "Function" [ "Polynomial", "Sinusoid", "Arcsin", "Arctan" ] func { _flag = func; Option_edit caption labels value = this.Function labels?value; } function_widget = Function "Polynomial"; // "Polynomial (a[n], a[n-1], ... a[1], a[0])", // "Sinusoid (freq, phase, amp, bias)", // "Arcsin (width, centre, range, bias)", // "Arctan (slope, centre, range, bias)" Gravity gravity = class Option_string "Gravity" [ "None", "Center", "East", "Forget", "NorthEast", "North", "NorthWest", "SouthEast", "South", "SouthWest", "West", "Static" ] gravity { _flag = "-gravity " ++ gravity; Option_edit caption labels value = this.Gravity labels?value; } gravity_widget = Gravity "Center"; ImageType imagetype = class Option_string "Image type" [ "Bilevel", "ColorSeparation", "ColorSeparationAlpha", "ColorSeparationMatte", "Grayscale", "GrayscaleAlpha", "GrayscaleMatte", "Optimize", "Palette", "PaletteBilevelAlpha", "PaletteBilevelMatte", "PaletteAlpha", "PaletteMatte", "TrueColorAlpha", "TrueColorMatte", "TrueColor" ] imagetype { _flag = "-type " ++ imagetype; Option_edit caption labels value = this.ImageType labels?value; } imagetype_widget = ImageType "TrueColor"; Intensity intensity = class Option_string "Intensity (gray conversion)" [ "Average", "Brightness", "Lightness", "MS", "Rec601Luma", "Rec601Luminance", "Rec709Luma", "Rec709Luminance", "RMS" ] intensity { _flag = "-intensity " ++ intensity, has_intensity = ""; Option_edit caption labels value = this.Intensity labels?value; } intensity_widget = Intensity "Rec709Luminance"; Interpolate interp = class Option_string "Interpolate" [ "default", "Average", "Average4", "Average9", "Average16", "Background", "Bilinear", "Blend", "Integer", "Mesh", "Nearest", "NearestNeighbor", "Spline" ] interp { _flag = if interp == "default" then "" else "-interpolate " ++ interp; Option_edit caption labels value = this.Interpolate labels?value; } interpolate_widget = Interpolate "default"; Kernel kernel = class Option_string "Kernel" [ "Unity", "Gaussian", "DoG", "LoG", "Blur", "Comet", "Binomial", "Laplacian", "Sobel", "FreiChen", "Roberts", "Prewitt", "Compass", "Kirsch", "Diamond", "Square", "Rectangle", "Disk", "Octagon", "Plus", "Cross", "Ring", "Peaks", "Edges", "Corners", "Diagonals", "LineEnds", "LineJunctions", "Ridges", "ConvexHull", "ThinSe", "Skeleton", "Chebyshev", "Manhattan", "Octagonal", "Euclidean" // FIXME: custom kernel ] kernel { _flag = kernel; Option_edit caption labels value = this.Kernel labels?value; } kernel_widget = Kernel "Unity"; ModColSp msp = class Option_string "modulate colorspace" [ "HCL", "HCLp", "HSB", "HSI", "HSL", "HSV", "HWB", "LCH" ] msp { _flag = "-set option:modulate:colorspace " ++ msp; Option_edit caption labels value = this.ModColSp labels?value; } ModColSp_widget = ModColSp "HSL"; MorphMeth morph = class Option_string "Method" [ "Correlate", "Convolve", "Dilate", "Erode", "Close", "Open", "DilateIntensity", "ErodeIntensity", "CloseIntensity", "OpenIntensity", "Smooth", "EdgeOut", "EdgeIn", "Edge", "TopHat", "BottomHat", "HitAndMiss", "Thinning", "Thicken", "Distance", "IterativeDistance" ] morph { _flag = morph; Option_edit caption labels value = this.MorphMeth labels?value; } morphmeth_widget = MorphMeth "Dilate"; Noise noise = class Option_string "Noise" [ "Gaussian", "Impulse", "Laplacian", "Multiplicative", "Poisson", "Random", "Uniform" ] noise { _flag = "+noise " ++ noise; Option_edit caption labels value = this.Noise labels?value; } noise_widget = Noise "Gaussian"; Pattern pattern = class Option_string "Noise" [ // See http://www.imagemagick.org/script/formats.php "bricks", "checkerboard", "circles", "crosshatch", "crosshatch30", "crosshatch45", "gray0", "gray5", "gray10", "gray15", "gray20", "gray25", "gray30", "gray35", "gray40", "gray45", "gray50", "gray55", "gray60", "gray65", "gray70", "gray75", "gray80", "gray85", "gray90", "gray95", "gray100", "hexagons", "horizontal", "horizontal2", "horizontal3", "horizontalsaw", "hs_bdiagonal", "hs_cross", "hs_diagcross", "hs_fdiagonal", "hs_horizontal", "hs_vertical", "left30", "left45", "leftshingle", "octagons", "right30", "right45", "rightshingle", "smallfishscales", "vertical", "vertical2", "vertical3", "verticalbricks", "verticalleftshingle", "verticalrightshingle", "verticalsaw" ] pattern { _flag = "pattern:" ++ pattern; Option_edit caption labels value = this.Pattern labels?value; } pattern_widget = Pattern "bricks"; ResizeType resizet = class Option_string "Resize type" [ "resize", "scale", "sample", "adaptive-resize" ] resizet { _flag = resizet; Option_edit caption labels value = this.ResizeType labels?value; } ResizeType_widget = ResizeType "resize"; Size_widget = class { _vislevel = 3; width = Expression "Width (pixels)" 64; height = Expression "Height (pixels)" 64; _flag = "-size " ++ print width.expr ++ "x" ++ print height.expr; }; StatType statt = class Option_string "Statistic type" [ "Gradient", "Maximum", "Mean", "Median", "Minimum", "Mode", "Nonpeak", "StandardDeviation" ] statt { _flag = statt; Option_edit caption labels value = this.StatType labels?value; } StatType_widget = StatType "Mean"; VirtualPixel vp = class Option_string "Virtual pixel" [ "Background", "Black", "CheckerTile", "Dither", "Edge", "Gray", "HorizontalTile", "HorizontalTileEdge", "Mirror", "None", "Random", "Tile", "Transparent", "VerticalTile", "VerticalTileEdge", "White" ] vp { _flag = "-virtual-pixel " ++ vp; _isBackground = (vp == "Background"); Option_edit caption labels value = this.VirtualPixel labels?value; } VirtualPixel_widget = VirtualPixel "Edge"; VirtualPixelBack_widget = class { virtpix = Magick.VirtualPixel_widget; background = Magick.background_widget; _flag = (if virtpix._isBackground then (background._flag ++ " ") else "") ++ virtpix._flag; } Geometry_widget = class { _vislevel = 3; x = Expression "X" 0; y = Expression "Y" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; AnnotGeometry_widget = class { _vislevel = 3; shearX = Expression "shear X (degrees)" 0; shearY = Expression "shear Y (degrees)" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print shearX.expr, "x", print shearY.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; OffsetGeometry_widget = class { _vislevel = 3; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; WhxyGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; hoffset = Expression "Horizontal offset" 0; voffset = Expression "Vertical offset" 0; _flag = concat [print x.expr, "x", print y.expr, format hoffset, format voffset] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; FrameGeometry_widget = class { _vislevel = 3; x = Expression "Width" 0; y = Expression "Height" 0; outbev = Expression "Outer bevel thickness" 0; inbev = Expression "Inner bevel thickness" 0; _flag = concat [print x.expr, "x", print y.expr, format outbev, format inbev] { // print an offset ... we want '+' in front of +ve strings format offset = concat ["+", print offset.expr], offset.expr >= 0 = print offset.expr; } }; Font_widget = class { _vislevel = 3; family = Option_string "Family" [ "Arial", "ArialBlack", "AvantGarde", "BitstreamCharter", "Bookman", "CenturySchoolbook", "ComicSansMS", "Courier", "CourierNew", "DejaVuSans", "DejaVuSansMono", "DejaVuSerif", "Dingbats", "FreeMono", "FreeSans", "FreeSerif", "Garuda", "Georgia", "Helvetica", "HelveticaNarrow", "Impact", "LiberationMono", "LiberationSans", "LiberationSerif", "NewCenturySchlbk", "Palatino", "Purisa", "Symbol", "Times", "TimesNewRoman", "Ubuntu", "Verdana", "Webdings" ] "Arial"; style = Option_string "Style" [ "Any", "Italic", "Normal", "Oblique" ] "Normal"; weight = Scale "Weight" 1 800 400; size = Scale "Point size" 1 100 12; stretch = Option_string "Stretch" [ "Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded", "Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed", "UltraExpanded" ] "Normal"; _flag = join_sep " " [ "-family", family.item, "-weight", print weight.value, "-pointsize", print size.value, "-style", style.item, "-stretch", stretch.item]; } } ================================================ FILE: share/nip2/start/_predicate.def ================================================ /* is_colour_space str: is a string one of nip's colour space names */ is_colour_space str = Image_type.colour_spaces.present 0 str; /* is_colour_type n: is a number one of VIPS's colour spaces */ is_colour_type n = Image_type.colour_spaces.present 1 n; /* is_number: is a real or a complex number. */ is_number a = is_real a || is_complex a; /* is_int: is an integer */ is_int a = is_real a && a == (int) a; /* is_uint: is an unsigned integer */ is_uint a = is_int a && a >= 0; /* is_pint: is a positive integer */ is_pint a = is_int a && a > 0; /* is_preal: is a positive real */ is_preal a = is_real a && a > 0; /* is_ureal: is an unsigned real */ is_ureal a = is_real a && a >= 0; /* is_letter c: true if character c is an ASCII letter * * is_letter :: char -> bool */ is_letter c = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); /* is_digit c: true if character c is an ASCII digit * * is_digit :: char->bool */ is_digit x = '0' <= x && x <= '9'; /* A whitespace character. * * is_space :: char->bool */ is_space = member " \n\t"; /* List str starts with section prefix. * * is_prefix "hell" "hello world!" == true * is_prefix :: [*] -> [*] -> bool */ is_prefix prefix str = take (len prefix) str == prefix; /* List str ends with section suffix. * * is_suffix "ld!" "hello world!" == true * is_suffix :: [*] -> [*] -> bool */ is_suffix suffix str = take (len suffix) (reverse str) == reverse suffix; /* List contains seqence. * * is_substr "llo" "hello world!" == true * is_substr :: [*] -> [*] -> bool */ is_substr seq str = any (map (is_prefix seq) (iterate tl str)); /* is_listof p s: true if finite list with p true for every element. */ is_listof p l = is_list l && all (map p l); /* is_string s: true if finite list of char. */ is_string s = is_listof is_char s; /* is_real_list l: is l a list of real numbers ... test each element, * so no infinite lists pls. */ is_real_list l = is_listof is_real l; /* is_string_list l: is l a finite list of finite strings. */ is_string_list l = is_listof is_string l; /* Test list length ... quicker than len x == n for large lists. */ is_list_len n x = true, x == [] && n == 0 = false, x == [] || n == 0 = is_list_len (n - 1) (tl x); is_list_len_more n x = true, x != [] && n == 0 = false, x == [] || n == 0 = is_list_len_more (n - 1) (tl x); is_list_len_more_equal n x = true, n == 0 = false, x == [] = is_list_len_more_equal (n - 1) (tl x); /* is_rectangular l: is l a rectangular data structure */ is_rectangular l = true, !is_list l = true, all (map is_obj l) = true, all (map is_list l) && all (map (not @ is_obj) l) && all (map is_rectangular l) && is_list_len_more 0 l && all (map (is_list_len (len (hd l))) (tl l)) = false { // treat strings as a base type, not [char] is_obj x = !is_list x || is_string x; } /* is_matrix l: is l a list of lists of real numbers, all the same length * * [[]] is the empty matrix, [] is the empty list ... disallow [] */ is_matrix l = l != [] && is_listof is_real_list l && is_rectangular l; /* is_square_matrix l: is l a matrix with width == height */ is_square_matrix l = true, l == [[]] = is_matrix l && is_list_len (len (hd l)) l; /* is_oddmatrix l: is l a matrix with odd-length sides */ is_oddmatrix l = true, l == [[]] = is_matrix l && len l % 2 == 1 && len l?0 % 2 == 1; /* is_odd_square_matrix l: is l a square_matrix with odd-length sides */ is_odd_square_matrix l = is_square_matrix l && len l % 2 == 1; /* Is an item in a column of a table? */ is_incolumn n table x = member (map (extract n) table) x; /* Is HGuide or VGuide. */ is_HGuide x = is_instanceof "HGuide" x; is_VGuide x = is_instanceof "VGuide" x; is_Guide x = is_HGuide x || is_VGuide x; is_Mark x = is_instanceof "Mark" x; is_Group x = is_instanceof "Group" x; is_NULL x = is_instanceof "NULL" x; is_List x = is_instanceof "List" x; is_Image x = is_instanceof "Image" x; is_Plot x = is_instanceof "Plot" x; is_Region x = is_instanceof "Region" x; is_Real x = is_instanceof "Real" x; is_Matrix x = is_instanceof "Matrix_base" x; is_Vector x = is_instanceof "Vector" x; is_Colour x = is_instanceof "Colour" x; is_Arrow x = is_instanceof "Arrow" x; is_Bool x = is_instanceof "Bool" x; is_Scale x = is_instanceof "Scale" x; is_Rect x = is_instanceof "Rect" x; is_Number x = is_instanceof "Number" x; is_Expression x = is_instanceof "Expression" x; is_String x = is_instanceof "String" x; /* A list of the form [[1,2],[3,4],[5,6]...] */ is_xy_list l = is_list l && all (map xy l) { xy l = is_real_list l && is_list_len 2 l; } // does a nested list structure contain a Group object? contains_Group l = true, is_list l && any (map is_Group l) = any (map contains_Group l), is_list l = false; /* Does an object have a sensible VIPS type? */ has_type x = is_image x || is_Image x || is_Arrow x || is_Colour x; /* Try to get a VIPS image type from an object. */ get_type x = get_type_im x, is_image x = get_type_im x.value, is_Image x = get_type_im x.image.value, is_Arrow x = Image_type.colour_spaces.lookup 0 1 x.colour_space, is_Colour x // slightly odd ... but our display is always 0-255, so it makes sense for // a plain number to be in the same range = Image_type.sRGB, is_real x = oo_unary_function get_type_op x, is_class x = error (_ "bad arguments to " ++ "get_type") { get_type_op = Operator "get_type" get_type Operator_type.COMPOUND false; // get the type from a VIPS image ... but only if it makes sense with // the rest of the image // we often have Type set wrong, hence the ugly guessing :-( // can have alpha, hence we let bands be one more than you might think get_type_im im = Image_type.LABQ, coding == Image_coding.LABPACK = Image_type.scRGB, coding == Image_coding.RAD = Image_type.GREY16, type == Image_type.GREY16 && is_bands 1 = Image_type.HISTOGRAM, type == Image_type.HISTOGRAM && (width == 1 || height == 1) = Image_type.B_W, is_bands 1 = Image_type.CMYK, type == Image_type.CMYK && is_bands 4 = type, is_colorimetric && is_bands 3 = Image_type.sRGB, !is_colorimetric && is_bands 3 = Image_type.MULTIBAND, !is_colorimetric && !is_bands 3 = type { type = get_header "Type" im; coding = get_header "Coding" im; bands = get_header "Bands" im; width = get_header "Xsize" im; height = get_header "Ysize" im; // 3-band colorimetric types we allow ... the things which the // Colour/Convert To menu can make, excluding mono. ok_types = [ Image_type.sRGB, Image_type.scRGB, Image_type.RGB16, Image_type.LAB, Image_type.LABQ, Image_type.LABS, Image_type.LCH, Image_type.XYZ, Image_type.YXY, Image_type.UCS ]; is_colorimetric = member ok_types type; // is bands n, with an optional alpha (ie. can be n + 1 too) is_bands n = bands == n || bands == n + 1; } } has_format x = has_member "format" x || is_Arrow x || is_image x; get_format x = x.format, has_member "format" x = x.image.format, is_Arrow x = get_header "BandFmt" x, is_image x = oo_unary_function get_format_op x, is_class x = error (_ "bad arguments to " ++ "get_format") { get_format_op = Operator "get_format" get_format Operator_type.COMPOUND false; } has_bits x = has_member "bits" x || is_Arrow x || is_image x; get_bits x = x.bits, has_member "bits" x = x.image.bits, is_Arrow x = get_header "Bbits" x, is_image x = oo_unary_function get_bits_op x, is_class x = error (_ "bad arguments to " ++ "get_bits") { get_bits_op = Operator "get_bits" get_format Operator_type.COMPOUND false; } has_bands x = is_image x || has_member "bands" x || is_Arrow x; get_bands x = x.bands, has_member "bands" x = x.image.bands, is_Arrow x = get_header "Bands" x, is_image x = 1, is_real x = len x, is_real_list x = oo_unary_function get_bands_op x, is_class x = error (_ "bad arguments to " ++ "get_bands") { get_bands_op = Operator "get_bands" get_bands Operator_type.COMPOUND false; } has_coding x = has_member "coding" x || is_Arrow x || is_image x; get_coding x = x.coding, has_member "coding" x = x.image.coding, is_Arrow x = get_header "Coding" x, is_image x = Image_coding.NOCODING, is_real x = oo_unary_function get_coding_op x, is_class x = error (_ "bad arguments to " ++ "get_coding") { get_coding_op = Operator "get_coding" get_coding Operator_type.COMPOUND false; } has_xres x = has_member "xres" x || is_Arrow x || is_image x; get_xres x = x.xres, has_member "xres" x = x.image.xres, is_Arrow x = get_header "Xres" x, is_image x = oo_unary_function get_xres_op x, is_class x = error (_ "bad arguments to " ++ "get_xres") { get_xres_op = Operator "get_xres" get_xres Operator_type.COMPOUND false; } has_yres x = has_member "yres" x || is_Arrow x || is_image x; get_yres x = x.yres, has_member "yres" x = x.image.yres, is_Arrow x = get_header "Yres" x, is_image x = oo_unary_function get_yres_op x, is_class x = error (_ "bad arguments to " ++ "get_yres") { get_yres_op = Operator "get_yres" get_yres Operator_type.COMPOUND false; } has_xoffset x = has_member "xoffset" x || is_Arrow x || is_image x; get_xoffset x = x.xoffset, has_member "xoffset" x = x.image.xoffset, is_Arrow x = get_header "Xoffset" x, is_image x = oo_unary_function get_xoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_xoffset") { get_xoffset_op = Operator "get_xoffset" get_xoffset Operator_type.COMPOUND false; } has_yoffset x = has_member "yoffset" x || is_Arrow x || is_image x; get_yoffset x = x.yoffset, has_member "yoffset" x = x.image.yoffset, is_Arrow x = get_header "Yoffset" x, is_image x = oo_unary_function get_yoffset_op x, is_class x = error (_ "bad arguments to " ++ "get_yoffset") { get_yoffset_op = Operator "get_yoffset" get_yoffset Operator_type.COMPOUND false; } has_value = has_member "value"; get_value x = x.value; has_image x = is_image x || is_Image x || is_Arrow x; get_image x = x.value, is_Image x = x.image.value, is_Arrow x = x, is_image x = oo_unary_function get_image_op x, is_class x = error (_ "bad arguments to " ++ "get_image") { get_image_op = Operator "get_image" get_image Operator_type.COMPOUND false; } has_number x = is_number x || is_Real x; get_number x = x.value, is_Real x = x, is_number x = oo_unary_function get_number_op x, is_class x = error (_ "bad arguments to " ++ "get_number") { get_number_op = Operator "get_number" get_number Operator_type.COMPOUND false; } has_real x = is_real x || is_Real x; get_real x = x.value, is_Real x = x, is_real x = oo_unary_function get_real_op x, is_class x = error (_ "bad arguments to " ++ "get_real") { get_real_op = Operator "get_real" get_real Operator_type.COMPOUND false; } has_width x = has_member "width" x || is_image x; get_width x = x.width, has_member "width" x = get_header "Xsize" x, is_image x = oo_unary_function get_width_op x, is_class x = error (_ "bad arguments to " ++ "get_width") { get_width_op = Operator "get_width" get_width Operator_type.COMPOUND false; } has_height x = has_member "height" x || is_image x; get_height x = x.height, has_member "height" x = get_header "Ysize" x, is_image x = oo_unary_function get_height_op x, is_class x = error (_ "bad arguments to " ++ "get_height") { get_height_op = Operator "get_height" get_height Operator_type.COMPOUND false; } has_left x = has_member "left" x; get_left x = x.left, has_member "left" x = oo_unary_function get_left_op x, is_class x = error (_ "bad arguments to " ++ "get_left") { get_left_op = Operator "get_left" get_left Operator_type.COMPOUND false; } has_top x = has_member "top" x; get_top x = x.top, has_member "top" x = oo_unary_function get_top_op x, is_class x = error (_ "bad arguments to " ++ "get_top") { get_top_op = Operator "get_top" get_top Operator_type.COMPOUND false; } // like has/get member, but first in a lst of objects has_member_list has objects = filter has objects != []; // need one with the args swapped get_member = converse dot; // get a member from the first of a list of objects to have it get_member_list has get objects = hd members, members != [] = error "unable to get property" { members = map get (filter has objects); } is_hist x = has_image x && (h == 1 || w == 1 || t == Image_type.HISTOGRAM) { im = get_image x; w = get_width im; h = get_height im; t = get_type im; } get_header field x = oo_unary_function get_header_op x, is_class x = get_header_image x, is_image x = error (_ "bad arguments to " ++ "get_header") { get_header_op = Operator "get_header" (get_header field) Operator_type.COMPOUND false; get_header_image im = im_header_int field im, type == itype = im_header_double field im, type == dtype = im_header_string field im, type == stype1 || type == stype2 = error (_ "image has no field " ++ field), type == 0 = error (_ "unknown type for field " ++ field) { type = im_header_get_typeof field im; itype = name2gtype "gint"; dtype = name2gtype "gdouble"; stype1 = name2gtype "VipsRefString"; stype2 = name2gtype "gchararray"; } } get_header_type field x = oo_unary_function get_header_type_op x, is_class x = im_header_get_typeof field x, is_image x = error (_ "bad arguments to " ++ "get_header_type") { get_header_type_op = Operator "get_header_type" (get_header_type field) Operator_type.COMPOUND false; } set_header field value x = oo_unary_function set_header_op x, is_class x = im_copy_set_meta x field value, is_image x = error (_ "bad arguments to " ++ "set_header") { set_header_op = Operator "set_header" (set_header field value) Operator_type.COMPOUND false; } ================================================ FILE: share/nip2/start/_stdenv.def ================================================ /* optional args to functions */ get_option options defaults f = error (_ "unknown parameter " ++ f), hits == [] = hits?0 { hits = [v :: [n, v] <- options ++ defaults; n == f]; } /* Various operators as functions. */ logical_and a b = a && b; logical_or a b = a || b; bitwise_and a b = a & b; bitwise_or a b = a | b; eor a b = a ^ b; left_shift a b = a << b; right_shift a b = a >> b; not a = !a; less a b = a < b; more a b = a > b; less_equal a b = a <= b; more_equal a b = a >= b; equal a b = a == b; not_equal a b = a != b; pointer_equal a b = a === b; not_pointer_equal a b = a !== b; add a b = a + b; subtract a b = a - b; multiply a b = a * b; divide a b = a / b; idivide a b = (int) ((int) a / (int) b); power a b = a ** b; square x = x * x; remainder a b = a % b; cons a b = a : b; dot a b = a . ( b ); join a b = a ++ b; // 'difference' is defined in _list subscript a b = a ? b; generate s n f = [s, n .. f]; comma r i = (r, i); compose f g = f @ g; // our only trinary operator is actually a binary operator if_then_else a x = if a then x?0 else x?1; cast_unsigned_char x = (unsigned char) x; cast_signed_char x = (signed char) x; cast_unsigned_short x = (unsigned short) x; cast_signed_short x = (signed short) x; cast_unsigned_int x = (unsigned int) x; cast_signed_int x = (signed int) x; cast_float x = (float) x; cast_double x = (double) x; cast_complex x = (complex) x; cast_double_complex x = (double complex) x; unary_minus x = -x; negate x = !x; complement x = ~x; unary_plus x = +x; // the function we call for "a -> v" expressions mksvpair s v = [s, v], is_string s = error "not str on lhs of ->"; // the vector ops ... im is an image, vec is a real_list vec op_name im vec = im_lintra_vec ones im vec, op_name == "add" || op_name == "add'" = im_lintra_vec ones (-1 * im) vec, op_name == "subtract'" = im_lintra_vec ones im inv, op_name == "subtract" = im_lintra_vec vec im zeros, op_name == "multiply" || op_name == "multiply'" = im_lintra_vec vec (1 / im) zeros, op_name == "divide'" = im_lintra_vec recip im zeros, op_name == "divide" = im_expntra_vec im vec, op_name == "power'" = im_powtra_vec im vec, op_name == "power" = im_remainderconst_vec im vec, op_name == "remainder" = im_andimage_vec im vec, op_name == "bitwise_and" || op_name == "bitwise_and'" = im_orimage_vec im vec, op_name == "bitwise_or" || op_name == "bitwise_or'" = im_eorimage_vec im vec, op_name == "eor" || op_name == "eor'" = im_equal_vec im vec, op_name == "equal" || op_name == "equal'" = im_notequal_vec im vec, op_name == "not_equal" || op_name == "not_equal'" = im_less_vec im vec, op_name == "less" = im_moreeq_vec im vec, op_name == "less'" = im_lesseq_vec im vec, op_name == "less_equal" = im_more_vec im vec, op_name == "less_equal'" = error ("unimplemented vector operation: " ++ op_name) { zeros = replicate (len vec) 0; ones = replicate (len vec) 1; recip = map (divide 1) vec; inv = map (multiply (-1)) vec; } // make a name value pair mknvpair n v = [n, v], is_string n = error "not [char] on LHS of =>"; /* Macbeth chart patch names. */ macbeth_names = [ "Dark skin", "Light skin", "Blue sky", "Foliage", "Blue flower", "Bluish green", "Orange", "Purplish blue", "Moderate red", "Purple", "Yellow green", "Orange yellow", "Blue", "Green", "Red", "Yellow", "Magenta", "Cyan", "White (density 0.05)", "Neutral 8 (density 0.23)", "Neutral 6.5 (density 0.44)", "Neutral 5 (density 0.70)", "Neutral 3.5 (density 1.05)", "Black (density 1.50)" ]; bandsplit x = oo_unary_function bandsplit_op x, is_class x = map (subscript x) [0 .. bands - 1], is_image x = error (_ "bad arguments to " ++ "bandsplit") { bands = get_header "Bands" x; bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) Operator_type.COMPOUND false; } bandjoin l = wrapper joined, has_wrapper = joined, is_listof has_image l = error (_ "bad arguments to " ++ "bandjoin") { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; joined = im_gbandjoin (map get_image l); } bandand x = oo_unary_function bandand_op x, is_class x = foldr1 bitwise_and (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandand") { bandand_op = Operator "bandand" bandand Operator_type.COMPOUND_REWRAP false; } bandor x = oo_unary_function bandor_op x, is_class x = foldr1 bitwise_or (bandsplit x), is_image x = error (_ "bad arguments to " ++ "bandor") { bandor_op = Operator "bandor" bandor Operator_type.COMPOUND_REWRAP false; } sum x = oo_unary_function sum_op x, is_class x = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x = sum_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") { sum_op = Operator "sum" sum Operator_type.COMPOUND false; // add elements in a nested-list thing sum_list l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } } product x = oo_unary_function product_op x, is_class x = product_list x, is_list x // (product image) doesn't make much sense :( = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") { product_op = Operator "product" product Operator_type.COMPOUND false; product_list l = foldr prod 1 l { prod x total = total * product x, is_list x = total * x; } } mean x = oo_unary_function mean_op x, is_class x = im_avg x, is_image x = mean_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") { mean_op = Operator "mean" mean Operator_type.COMPOUND false; mean_list l = sum l / size l; // number of elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1; } } meang x = (appl (power e) @ mean @ appl log) x { appl fn x = map fn x, is_list x = fn x; } skew x = oo_unary_function skew_op x, is_class x = sum ((x - m) ** 3) / ((N - 1) * s ** 3), is_image x = sum ((Group x' - m) ** 3).value / ((N - 1) * s ** 3), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "skew") { skew_op = Operator "skew" skew Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = w * h * b, is_image x' = len x'; } kurtosis x = oo_unary_function kurtosis_op x, is_class x = sum ((x - m) ** 4) / ((N - 1) * s ** 4), is_image x = sum ((Group x' - m) ** 4).value / ((N - 1) * s ** 4), is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "kurtosis") { kurtosis_op = Operator "kurtosis" kurtosis Operator_type.COMPOUND false; // squash any large matrix down to a flat list ... much simpler x' = x, is_image x; = flatten x; m = mean x'; s = deviation x'; w = get_width x'; h = get_height x'; b = get_bands x'; N = len x', is_list x'; = w * h * b; } // zero-excluding mean meanze x = oo_unary_function meanze_op x, is_class x = meanze_image_hist x, is_image x && (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) = meanze_image x, is_image x = meanze_list x, is_list x = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") { fmt = get_format x; meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; meanze_list l = sum l / size l; // number of non-zero elements in some sort of nested-list thing size l = foldr acc 0 l { acc x total = total + size x, is_list x = total + 1, x != 0; = total; } // add elements in a nested-list thing sum l = foldr acc 0 l { acc x total = total + sum x, is_list x = total + x; } // image mean, for any image type meanze_image i = sum / N { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } // image mean for 8 and 16-bit unsigned images // we can use a histogram, yay, and save a pass through the image meanze_image_hist i = sum / N { // histogram, knock out zeros hist = hist_find i; black = image_new 1 1 (get_bands hist) (get_format hist) (get_coding hist) (get_type hist) 0 0 0; histze = insert 0 0 black hist; // matching identity iden = im_identity_ushort (get_bands hist) (get_width hist), (get_width hist) > 256 = im_identity (get_bands hist); // number of non-zero pixels N = mean histze * 256; // sum of pixels sum = mean (hist * iden) * 256; } } deviation x = oo_unary_function deviation_op x, is_class x = im_deviate x, is_image x = deviation_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviation") { deviation_op = Operator "deviation" deviation Operator_type.COMPOUND false; deviation_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return n, sum, sum of squares for a list of reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } } deviationze x = oo_unary_function deviationze_op x, is_class x = deviationze_image x, is_image x = deviationze_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "deviationze") { deviationze_op = Operator "deviationze" deviationze Operator_type.COMPOUND false; deviationze_list l = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 { [n, s, s2] = sum_sum2_list l; } // return number of non-zero elements, sum, sum of squares for a list of // reals sum_sum2_list x = foldr accumulate [0, 0, 0] x { accumulate x sofar = sofar, is_real x && x == 0 = [n + 1, x + s, x * x + s2], is_real x = [n + n', s + s', s2 + s2'], is_list x = error "sum_sum2_list: not real or [real]" { [n, s, s2] = sofar; [n', s', s2'] = sum_sum2_list x; } } deviationze_image i = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 { w = get_width i; h = get_height i; b = get_bands i; st = stats i; sum = st.value?0?2; sum2 = st.value?0?3; // find non-zero pixels (not zero in all bands) zp = im_notequal_vec i (replicate b 0); // number of non-zero pixels N = b * (mean zp * w * h) / 255; } } // find the centre of gravity of a histogram gravity x = oo_unary_function gravity_op x, is_class x = im_hist_gravity x, is_hist x = gravity_list x, is_list x = error (_ "bad arguments to " ++ "gravity") { gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; // centre of gravity of a histogram... use the histogram to weight an // identity, then sum, then find the mean element im_hist_gravity h = m { // make horizontal h' = rot270 h, get_width h == 1 = h, get_height h == 1 = error "width or height not 1"; // number of elements w = get_width h'; // matching identity i = im_identity_ushort 1 w, w <= 2 ** 16 - 1 = make_xy w 1 ? 0; // weight identity and sum s = mean (i * h') * w; // sum of original histogram s' = mean h * w; // weighted mean m = s / s'; } gravity_list l = m { w = len l; // matching identity i = [0, 1 .. w - 1]; // weight identity and sum s = sum (map2 multiply i l); // sum of original histogram s' = sum l; // weighted mean m = s / s'; } } project x = oo_unary_function project_op x, is_class x = im_project x, is_image x = error (_ "bad arguments to " ++ "project") { project_op = Operator "project" project Operator_type.COMPOUND false; } abs x = oo_unary_function abs_op x, is_class x = im_abs x, is_image x = abs_cmplx x, is_complex x = abs_num x, is_real x = abs_list x, is_real_list x = abs_list (map abs_list x), is_matrix x = error (_ "bad arguments to " ++ "abs") { abs_op = Operator "abs" abs Operator_type.COMPOUND false; abs_list l = (sum (map square l)) ** 0.5; abs_num n = n, n >= 0 = -n; abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; } copy x = oo_unary_function copy_op x, is_class x = im_copy x, is_image x = x { copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; } // like abs, but treat pixels as vectors ... ie. always get a 1-band image // back ... also treat matricies as lists of vectors // handy for dE from object difference abs_vec x = oo_unary_function abs_vec_op x, is_class x = abs_vec_image x, is_image x = abs_vec_cmplx x, is_complex x = abs_vec_num x, is_real x = abs_vec_list x, is_real_list x = mean (map abs_vec_list x), is_matrix x = error (_ "bad arguments to " ++ "abs_vec") { abs_vec_op = Operator "abs_vec" abs_vec Operator_type.COMPOUND false; abs_vec_list l = (sum (map square l)) ** 0.5; abs_vec_num n = n, n >= 0 = -n; abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; abs_vec_image im = (sum (map square (bandsplit im))) ** 0.5; } transpose x = oo_unary_function transpose_op x, is_class x = transpose_image x, is_image x = transpose_list x, is_listof is_list x = error (_ "bad arguments to " ++ "transpose") { transpose_op = Operator "transpose" transpose Operator_type.COMPOUND_REWRAP false; transpose_list l = [], l' == [] = (map hd l') : (transpose_list (map tl l')) { l' = takewhile (not_equal []) l; } transpose_image = im_flipver @ im_rot270; } rot45 x = oo_unary_function rot45_op x, is_class x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45") { rot45_op = Operator "rot45" rot45_object Operator_type.COMPOUND_REWRAP false; rot45_object x = rot45_matrix x, is_odd_square_matrix x = error "rot45 image: not implemented", is_image x = error (_ "bad arguments to " ++ "rot45"); // slow, but what the heck rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; } // apply an image function to a [[real]] ... matrix is converted to a 1 band // image for processing apply_matrix_as_image fn m = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; // a general image/matrix operation where the mat version is most easily done // by converting mat->image->mat apply_matim_operation name fn x = oo_unary_function class_op x, is_class x = fn x, is_image x = apply_matrix_as_image fn x, is_matrix x = error (_ "bad arguments to " ++ name) { class_op = Operator name (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; } rot90 = apply_matim_operation "rot90" im_rot90; rot180 = apply_matim_operation "rot180" im_rot180; rot270 = apply_matim_operation "rot270" im_rot270; rotquad = apply_matim_operation "rotquad" im_rotquad; fliplr = apply_matim_operation "fliplr" im_fliphor; fliptb = apply_matim_operation "flipud" im_flipver; image_set_type type x = oo_unary_function image_set_type_op x, is_class x = im_copy_set x (to_real type) (get_header "Xres" x) (get_header "Yres" x) (get_header "Xoffset" x) (get_header "Yoffset" x), is_image x = error (_ "bad arguments to " ++ "image_set_type:" ++ print type ++ " " ++ print x) { image_set_type_op = Operator "image_set_type" (image_set_type type) Operator_type.COMPOUND_REWRAP false; } image_set_origin xoff yoff x = oo_unary_function image_set_origin_op x, is_class x = im_copy_set x (get_header "Type" x) (get_header "Xres" x) (get_header "Yres" x) (to_real xoff) (to_real yoff), is_image x = error (_ "bad arguments to " ++ "image_set_origin") { image_set_origin_op = Operator "image_set_origin" (image_set_origin xoff yoff) Operator_type.COMPOUND_REWRAP false; } cache tile_width tile_height max_tiles x = oo_unary_function cache_op x, is_class x = im_tile_cache_random x (to_real tile_width) (to_real tile_height) (to_real max_tiles), is_image x = error (_ "bad arguments to " ++ "cache") { cache_op = Operator "cache" (cache tile_width tile_height max_tiles) Operator_type.COMPOUND_REWRAP false; } tile across down x = oo_unary_function tile_op x, is_class x = im_replicate x (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "tile") { tile_op = Operator "tile" (tile across down) Operator_type.COMPOUND_REWRAP false; } grid tile_height across down x = oo_unary_function grid_op x, is_class x = im_grid x (to_real tile_height) (to_real across) (to_real down), is_image x = error (_ "bad arguments to " ++ "grid") { grid_op = Operator "grid" (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; } max_pair a b = a, a > b = b; min_pair a b = a, a < b = b; range min value max = min_pair max (max_pair min value); max x = oo_unary_function max_op x, is_class x = im_max x, is_image x = max_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "max") { max_op = Operator "max" max Operator_type.COMPOUND false; max_list x = error "max []", x == [] = foldr1 max_pair x, is_real_list x = foldr1 max_pair (map max_list x), is_list x = max x; } min x = oo_unary_function min_op x, is_class x = im_min x, is_image x = min_list x, is_list x = x, is_number x = error (_ "bad arguments to " ++ "min") { min_op = Operator "min" min Operator_type.COMPOUND false; min_list x = error "min []", x == [] = foldr1 min_pair x, is_real_list x = foldr1 min_pair (map min_list x), is_list x = min x; } maxpos x = oo_unary_function maxpos_op x, is_class x = im_maxpos x, is_image x = maxpos_matrix x, is_matrix x = maxpos_list x, is_list x = error (_ "bad arguments to " ++ "maxpos") { maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; maxpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { max_value = max (Matrix m); indexes = map (index (equal max_value)) m; row = index (not_equal (-1)) indexes; } maxpos_list l = -1, l == [] = index (equal (max l)) l; } minpos x = oo_unary_function minpos_op x, is_class x = im_minpos x, is_image x = minpos_matrix x, is_matrix x = minpos_list x, is_list x = error (_ "bad arguments to " ++ "minpos") { minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; minpos_matrix m = (-1, -1), m == [[]] = (indexes?row, row) { min_value = min (Matrix m); indexes = map (index (equal min_value)) m; row = index (not_equal (-1)) indexes; } minpos_list l = -1, l == [] = index (equal (min l)) l; } stats x = oo_unary_function stats_op x, is_class x = im_stats x, is_image x = im_stats (to_image x).value, is_matrix x = error (_ "bad arguments to " ++ "stats") { stats_op = Operator "stats" stats Operator_type.COMPOUND false; } e = 2.7182818284590452354; pi = 3.14159265358979323846; rad d = 2 * pi * (d / 360); deg r = 360 * r / (2 * pi); sign x = oo_unary_function sign_op x, is_class x = im_sign x, is_image x = sign_cmplx x, is_complex x = sign_num x, is_real x = error (_ "bad arguments to " ++ "sign") { sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; sign_num n = 0, n == 0 = 1, n > 0 = -1; sign_cmplx c = (0, 0), mod == 0 = (re c / mod, im c / mod) { mod = abs c; } } rint x = oo_unary_function rint_op x, is_class x = im_rint x, is_image x = rint_value x, is_number x = error (_ "bad arguments to " ++ "rint") { rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; rint_value x = (int) (x + 0.5), x > 0 = (int) (x - 0.5); } scale x = oo_unary_function scale_op x, is_class x = (unsigned char) x, is_number x = im_scale x, is_image x = scale_list x, is_real_list x || is_matrix x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; scale_list l = apply_scale s o l { mn = find_limit min_pair l; mx = find_limit max_pair l; s = 255.0 / (mx - mn); o = -(mn * s); } find_limit fn l = find_limit fn (map (find_limit fn) l), is_listof is_list l = foldr1 fn l; apply_scale s o x = x * s + o, is_number x = map (apply_scale s o) x; } scaleps x = oo_unary_function scale_op x, is_class x = im_scaleps x, is_image x = error (_ "bad arguments to " ++ "scale") { scale_op = Operator "scaleps" scaleps Operator_type.COMPOUND_REWRAP false; } fwfft x = oo_unary_function fwfft_op x, is_class x = im_fwfft x, is_image x = error (_ "bad arguments to " ++ "fwfft") { fwfft_op = Operator "fwfft" fwfft Operator_type.COMPOUND_REWRAP false; } invfft x = oo_unary_function invfft_op x, is_class x = im_invfftr x, is_image x = error (_ "bad arguments to " ++ "invfft") { invfft_op = Operator "invfft" invfft Operator_type.COMPOUND_REWRAP false; } falsecolour x = oo_unary_function falsecolour_op x, is_class x = image_set_type Image_type.sRGB (im_falsecolour x), is_image x = error (_ "bad arguments to " ++ "falsecolour") { falsecolour_op = Operator "falsecolour" falsecolour Operator_type.COMPOUND_REWRAP false; } polar x = oo_unary_function polar_op x, is_class x = im_c2amph x, is_image x = polar_cmplx x, is_complex x = error (_ "bad arguments to " ++ "polar") { polar_op = Operator "polar" polar Operator_type.COMPOUND false; polar_cmplx r = (l, a) { a = 270, x == 0 && y < 0 = 90, x == 0 && y >= 0 = 360 + atan (y / x), x > 0 && y < 0 = atan (y / x), x > 0 && y >= 0 = 180 + atan (y / x); l = (x ** 2 + y ** 2) ** 0.5; x = re r; y = im r; } } rectangular x = oo_unary_function rectangular_op x, is_class x = im_c2rect x, is_image x = rectangular_cmplx x, is_complex x = error (_ "bad arguments to " ++ "rectangular") { rectangular_op = Operator "rectangular" rectangular Operator_type.COMPOUND false; rectangular_cmplx p = (x, y) { l = re p; a = im p; x = l * cos a; y = l * sin a; } } // we can't use colour_unary: that likes 3 band only recomb matrix x = oo_unary_function recomb_op x, is_class x = im_recomb x matrix, is_image x = recomb_real_list x, is_real_list x = map recomb_real_list x, is_matrix x = error (_ "bad arguments to " ++ "recomb") { // COMPOUND_REWRAP ... signal to the colour class to go to image and // back recomb_op = Operator "recomb" (recomb matrix) Operator_type.COMPOUND_REWRAP false; // process [1,2,3 ..] as an image recomb_real_list l = (to_matrix im').value?0 { im = (float) (to_image (Vector l)).value; im' = recomb matrix im; } } extract_area x y w h obj = oo_unary_function extract_area_op obj, is_class obj = im_extract_area obj x' y' w' h', is_image obj = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj = error (_ "bad arguments to " ++ "extract_area") { x' = to_real x; y' = to_real y; w' = to_real w; h' = to_real h; extract_area_op = Operator "extract_area" (extract_area x y w h) Operator_type.COMPOUND_REWRAP false; extract_range from length list = (take length @ drop from) list; } extract_band b obj = subscript obj b; extract_row y obj = oo_unary_function extract_row_op obj, is_class obj = extract_area 0 y' (get_width obj) 1 obj, is_image obj = [obj?y'], is_matrix obj = error (_ "bad arguments to " ++ "extract_row") { y' = to_real y; extract_row_op = Operator "extract_row" (extract_row y) Operator_type.COMPOUND_REWRAP false; } extract_column x obj = oo_unary_function extract_column_op obj, is_class obj = extract_area x' 0 1 height obj, is_image obj = map (\row [row?x']) obj, is_matrix obj = error (_ "bad arguments to " ++ "extract_column") { x' = to_real x; height = get_header "Ysize" obj; extract_column_op = Operator "extract_column" (extract_column x) Operator_type.COMPOUND_REWRAP false; } blend cond in1 in2 = oo_binary_function blend_op cond [in1,in2], is_class cond = im_blend (get_image cond) (get_image in1) (get_image in2), has_image cond && has_image in1 && has_image in2 = error (_ "bad arguments to " ++ "blend" ++ ": " ++ join_sep ", " (map print [cond, in1, in2])) { blend_op = Operator "blend" blend_obj Operator_type.COMPOUND_REWRAP false; blend_obj cond x = blend_result_image { [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, cond]; // properties of our output image target_width = get_member_list has_width get_width objects; target_height = get_member_list has_height get_height objects; target_bands = get_member_list has_bands get_bands objects; target_format = get_member_list has_format get_format objects; target_type = get_member_list has_type get_type objects; to_image x = to_image_size target_width target_height target_bands target_format x; [then_image, else_image] = map to_image [then_part, else_part]; blend_result_image = image_set_type target_type (im_blend cond then_image else_image); } } // do big first: we want to keep big's class, if possible // eg. big is a Plot, small is a 1x1 Image insert x y small big = oo_binary'_function insert_op small big, is_class big = oo_binary_function insert_op small big, is_class small = im_insert big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert") { insert_op = Operator "insert" (insert x y) Operator_type.COMPOUND_REWRAP false; } insert_noexpand x y small big = oo_binary_function insert_noexpand_op small big, is_class small = oo_binary'_function insert_noexpand_op small big, is_class big = im_insert_noexpand big small (to_real x) (to_real y), is_image small && is_image big = error (_ "bad arguments to " ++ "insert_noexpand") { insert_noexpand_op = Operator "insert_noexpand" (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; } decode im = oo_unary_function decode_op im, is_class im = decode_im im, is_image im = error (_ "bad arguments to " ++ "decode") { decode_op = Operator "decode" decode Operator_type.COMPOUND_REWRAP false; decode_im im = im_LabQ2Lab im, get_coding im == Image_coding.LABPACK = im_rad2float im, get_coding im == Image_coding.RAD = im; } measure_draw across down measure image = mark { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; x = map (extract 0) cods; y = map (extract 1) cods; outer = mkim [$pixel => 255] sample_width sample_height 1; inner = mkim [] (sample_width - 4) (sample_height - 4) 1; patch = insert 2 2 inner outer; bg = mkim [] image.width image.height 1; mask = Image (im_insertset bg.value patch.value x y); image' = colour_transform_to Image_type.sRGB image; mark = if mask then Vector [0, 255, 0] else image'; } measure_sample across down measure image = measures { patch_width = image.width / across; sample_width = patch_width * (measure / 100); left_margin = (patch_width - sample_width) / 2; patch_height = image.height / down; sample_height = patch_height * (measure / 100); top_margin = (patch_height - sample_height) / 2; cods = [[x * patch_width + left_margin, y * patch_height + top_margin] :: y <- [0 .. down - 1]; x <- [0 .. across - 1]]; image' = decode image; patches = map (\p extract_area p?0 p?1 sample_width sample_height image') cods; measures = Matrix (map (map mean) (map bandsplit patches)); } extract_bands b n obj = oo_unary_function extract_bands_op obj, is_class obj = im_extract_bands obj (to_real b) (to_real n), is_image obj = error (_ "bad arguments to " ++ "extract_bands") { extract_bands_op = Operator "extract_bands" (extract_bands b n) Operator_type.COMPOUND_REWRAP false; } invert x = oo_unary_function invert_op x, is_class x = im_invert x, is_image x = 255 - x, is_real x = error (_ "bad arguments to " ++ "invert") { invert_op = Operator "invert" invert Operator_type.COMPOUND false; } transform ipol wrap params image = oo_unary_function transform_op image, is_class image = im_transform image (to_matrix params) (to_real ipol) (to_real wrap), is_image image = error (_ "bad arguments to " ++ "transform") { transform_op = Operator "transform" (transform ipol wrap params) Operator_type.COMPOUND_REWRAP false; } transform_search max_error max_iterations order ipol wrap sample reference = oo_binary_function transform_search_op sample reference, is_class sample = oo_binary'_function transform_search_op sample reference, is_class reference = im_transform_search sample reference (to_real max_error) (to_real max_iterations) (to_real order) (to_real ipol) (to_real wrap), is_image sample && is_image reference = error (_ "bad arguments to " ++ "transform_search") { transform_search_op = Operator "transform_search" (transform_search max_error max_iterations order ipol wrap) Operator_type.COMPOUND false; } rotate interp angle image = oo_binary_function rotate_op angle image, is_class angle = oo_binary'_function rotate_op angle image, is_class image = im_affinei_all image interp.value a (-b) b a 0 0, is_real angle && is_image image = error (_ "bad arguments to " ++ "rotate") { rotate_op = Operator "rotate" (rotate interp) Operator_type.COMPOUND_REWRAP false; a = cos angle; b = sin angle; } matrix_binary fn a b = itom (fn (mtoi a) (mtoi b)) { mtoi x = im_mask2vips (Matrix x); itom x = (im_vips2mask x).value; } join_lr a b = oo_binary_function join_lr_op a b, is_class a = oo_binary'_function join_lr_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_lr") { join_lr_op = Operator "join_lr" join_lr Operator_type.COMPOUND_REWRAP false; join_im a b = insert (get_width a) 0 b a; } join_tb a b = oo_binary_function join_tb_op a b, is_class a = oo_binary'_function join_tb_op a b, is_class b = join_im a b, is_image a && is_image b = matrix_binary join_im a b, is_matrix a && is_matrix b = error (_ "bad arguments to " ++ "join_tb") { join_tb_op = Operator "join_tb" join_tb Operator_type.COMPOUND_REWRAP false; join_im a b = insert 0 (get_height a) b a; } conj x = oo_unary_function conj_op x, is_class x = (re x, -im x), is_complex x || (is_image x && format == Image_format.COMPLEX) || (is_image x && format == Image_format.DPCOMPLEX) // assume it's some sort of real = x { format = get_header "BandFmt" x; conj_op = Operator "conj" conj Operator_type.COMPOUND false; } clip2fmt format image = oo_unary_function clip2fmt_op image, is_class image = im_clip2fmt image (to_real format), is_image image = error (_ "bad arguments to " ++ "clip2fmt") { clip2fmt_op = Operator "clip2fmt" (clip2fmt format) Operator_type.COMPOUND_REWRAP false; } embed type x y w h im = oo_unary_function embed_op im, is_class im = im_embed im (to_real type) (to_real x) (to_real y) (to_real w) (to_real h), is_image im = error (_ "bad arguments to " ++ "embed") { embed_op = Operator "embed" (embed type x y w h) Operator_type.COMPOUND_REWRAP false; } /* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it * with m1, turn it back to a matrix again. */ _morph_2_masks fn m1 m2 = m'' { // The [[real]] can contain 128 values ... squeeze them out! image = im_mask2vips (Matrix m2) == 255; m2_width = get_width image; m2_height = get_height image; // need to embed m2 in an image large enough for us to be able to // position m1 all around the edges, with a 1 pixel overlap image' = embed 0 (m1.width / 2) (m1.height / 2) (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) image; // morph! image'' = fn m1 image'; // back to mask m' = im_vips2mask ((double) image''); // Turn 0 in output to 128 (don't care). m'' = map (map fn) m'.value { fn a = 128, a == 0; = a; } } dilate mask image = oo_unary_function dilate_op image, is_class image = im_dilate image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "dilate") { dilate_op = Operator "dilate" dilate_object Operator_type.COMPOUND_REWRAP false; dilate_object x = _morph_2_masks dilate mask x, is_matrix x = dilate mask x; } erode mask image = oo_unary_function erode_op image, is_class image = im_erode image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "erode") { erode_op = Operator "erode" erode_object Operator_type.COMPOUND_REWRAP false; erode_object x = _morph_2_masks erode mask x, is_matrix x = erode mask x; } conv mask image = oo_unary_function conv_op image, is_class image = im_conv image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "conv" ++ ": " ++ "conv (" ++ print mask ++ ") (" ++ print image ++ ")") { conv_op = Operator "conv" (conv mask) Operator_type.COMPOUND_REWRAP false; } convf mask image = oo_unary_function convf_op image, is_class image = im_conv_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convf" ++ ": " ++ "convf (" ++ print mask ++ ") (" ++ print image ++ ")") { convf_op = Operator "convf" (convf mask) Operator_type.COMPOUND_REWRAP false; } convsep mask image = oo_unary_function convsep_op image, is_class image = im_convsep image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsep") { convsep_op = Operator "convsep" (convsep mask) Operator_type.COMPOUND_REWRAP false; } aconvsep layers mask image = oo_unary_function aconvsep_op image, is_class image = im_aconvsep image (to_matrix mask) (to_real layers), is_image image = error (_ "bad arguments to " ++ "aconvsep") { aconvsep_op = Operator "aconvsep" (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; } convsepf mask image = oo_unary_function convsepf_op image, is_class image = im_convsep_f image (to_matrix mask), is_image image = error (_ "bad arguments to " ++ "convsepf") { convsepf_op = Operator "convsepf" (convsepf mask) Operator_type.COMPOUND_REWRAP false; } rank w h n image = oo_unary_function rank_op image, is_class image = im_rank image (to_real w) (to_real h) (to_real n), is_image image = error (_ "bad arguments to " ++ "rank") { rank_op = Operator "rank" (rank w h n) Operator_type.COMPOUND_REWRAP false; } rank_image n x = rlist x.value, is_Group x = rlist x, is_list x = error (_ "bad arguments to " ++ "rank_image") { rlist l = wrapper ranked, has_wrapper = ranked { has_wrapper = has_member_list (has_member "Image") l; wrapper = get_member_list (has_member "Image") (get_member "Image") l; ranked = im_rank_image (map get_image l) (to_real n); } } // find the correlation surface for a small image within a big one correlate small big = oo_binary_function correlate_op small big, is_class small = oo_binary'_function correlate_op small big, is_class big = im_spcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate") { correlate_op = Operator "correlate" correlate Operator_type.COMPOUND_REWRAP false; } // just sum-of-squares-of-differences correlate_fast small big = oo_binary_function correlate_fast_op small big, is_class small = oo_binary'_function correlate_fast_op small big, is_class big = im_fastcor big small, is_image small && is_image big = error (_ "bad arguments to " ++ "correlate_fast") { correlate_fast_op = Operator "correlate_fast" correlate_fast Operator_type.COMPOUND_REWRAP false; } // set Type, wrap as Plot_hist if it's a class hist_tag x = oo_unary_function hist_tag_op x, is_class x = image_set_type Image_type.HISTOGRAM x, is_image x = error (_ "bad arguments to " ++ "hist_tag") { hist_tag_op = Operator "hist_tag" (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; } hist_find x = oo_unary_function hist_find_op x, is_class x = im_histgr x (-1), is_image x = error (_ "bad arguments to " ++ "hist_find") { hist_find_op = Operator "hist_find" (Plot_histogram @ hist_find) Operator_type.COMPOUND false; } hist_find_nD bins image = oo_unary_function hist_find_nD_op image, is_class image = im_histnD image (to_real bins), is_image image = error (_ "bad arguments to " ++ "hist_find_nD") { hist_find_nD_op = Operator "hist_find_nD" (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; } hist_find_indexed mode index value = oo_binary_function hist_find_indexed_op index value, is_class index = oo_binary'_function hist_find_indexed_op index value, is_class value = indexed index value, is_image index && is_image value = error (_ "bad arguments to " ++ "hist_find_indexed") { hist_find_indexed_op = Operator "hist_find_indexed" (compose (compose Plot_histogram) (hist_find_indexed mode)) Operator_type.COMPOUND false; indexed index value = out { [out] = vips_call "hist_find_indexed" [value, index] [ "combine" => mode ]; } } hist_entropy x = oo_unary_function hist_entropy_op x, is_class x = entropy x, is_image x = error (_ "bad arguments to " ++ "hist_entropy") { hist_entropy_op = Operator "hist_entropy" hist_entropy Operator_type.COMPOUND_REWRAP false; entropy x = out { [out] = vips_call "hist_entropy" [x] [ ]; } } hist_map hist image = oo_binary_function hist_map_op hist image, is_class hist = oo_binary'_function hist_map_op hist image, is_class image = im_maplut image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "hist_map") { // can't use rewrap, as we want to always wrap as image hist_map_op = Operator "hist_map" (compose (compose Image) hist_map) Operator_type.COMPOUND false; } hist_cum hist = oo_unary_function hist_cum_op hist, is_class hist = im_histcum hist, is_image hist = error (_ "bad arguments to " ++ "hist_cum") { hist_cum_op = Operator "hist_cum" hist_cum Operator_type.COMPOUND_REWRAP false; } hist_diff hist = oo_unary_function hist_diff_op hist, is_class hist = im_histdiff hist, is_image hist = error (_ "bad arguments to " ++ "hist_diff") { hist_diff_op = Operator "hist_diff" hist_diff Operator_type.COMPOUND_REWRAP false; im_histdiff h = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h { // up the format so it can represent the whole range of // possible values from this mask fmt x = Image_format.SHORT, x == Image_format.UCHAR || x == Image_format.CHAR = Image_format.INT, x == Image_format.USHORT || x == Image_format.SHORT || x == Image_format.UINT = x; } } hist_norm hist = oo_unary_function hist_norm_op hist, is_class hist = im_histnorm hist, is_image hist = error (_ "bad arguments to " ++ "hist_norm") { hist_norm_op = Operator "hist_norm" hist_norm Operator_type.COMPOUND_REWRAP false; } hist_inv hist = oo_unary_function hist_inv_op hist, is_class hist = inv hist, is_image hist = error (_ "bad arguments to " ++ "hist_inv") { hist_inv_op = Operator "hist_inv" hist_inv Operator_type.COMPOUND_REWRAP false; inv im = im_invertlut (to_matrix im''') len { // need a vertical doublemask im' = rot90 im, get_width im > 1 && get_height im == 1 = im, get_width im == 1 && get_height im > 1 = error (_ "not a hist"); len = get_height im'; // values must be scaled to 0 - 1 im'' = im' / (max im'); // add an index column on the left // again, must be in 0-1 y = ((make_xy 1 len)?1) / len; im''' = y ++ im''; } } hist_match in ref = oo_binary_function hist_match_op in ref, is_class in = oo_binary'_function hist_match_op in ref, is_class ref = im_histspec in ref, is_image in && is_image ref = error (_ "bad arguments to " ++ "hist_match") { hist_match_op = Operator "hist_match" hist_match Operator_type.COMPOUND_REWRAP false; } hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; hist_equalize_local w h l image = oo_unary_function hist_equalize_local_op image, is_class image = out, is_image image = error (_ "bad arguments to " ++ "hist_equalize_local") { hist_equalize_local_op = Operator "hist_equalize_local" (hist_equalize_local w h l) Operator_type.COMPOUND_REWRAP false; [out] = vips_call "hist_local" [image, to_real w, to_real h] [$max_slope => to_real l]; } // find the threshold below which are percent of the image (percent in [0,1]) // eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels hist_thresh percent image = x { // our own normaliser ... we don't want to norm channels separately // norm to [0,1] my_hist_norm h = h / max h; // normalised cumulative hist // we sum the channels before we normalise, because we want to treat them // all the same h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) image.value; // threshold that, then use im_profile to search for the x position in the // histogram x = mean (im_profile (h > percent) 1); } /* Sometimes useful, despite plotting now being built in, for making * diagnostic images. */ hist_plot hist = oo_unary_function hist_plot_op hist, is_class hist = im_histplot hist, is_image hist = error (_ "bad arguments to " ++ "hist_plot") { hist_plot_op = Operator "hist_plot" (Image @ hist_plot) Operator_type.COMPOUND false; } zerox d x = oo_unary_function zerox_op x, is_class x = im_zerox x (to_real d), is_image x = error (_ "bad arguments to " ++ "zerox") { zerox_op = Operator "zerox" (zerox d) Operator_type.COMPOUND_REWRAP false; } reduce kernel xshr yshr image = oo_unary_function reduce_op image, is_class image = reduce_im image, is_image image = error (_ "bad arguments to " ++ "reduce") { reduce_op = Operator "reduce" reduce_im Operator_type.COMPOUND_REWRAP false; reduce_im im = out { [out] = vips_call "reduce" [im, xshr, yshr] [$kernel => kernel.value]; } } similarity interpolate scale angle image = oo_unary_function similarity_op image, is_class image = similarity_im image, is_image image = error (_ "bad arguments to " ++ "similarity") { similarity_op = Operator "similarity" similarity_im Operator_type.COMPOUND_REWRAP false; similarity_im im = out { [out] = vips_call "similarity" [im] [ $interpolate => interpolate.value, $scale => scale, $angle => angle ]; } } resize kernel xfac yfac image = oo_unary_function resize_op image, is_class image = resize_im image, is_image image = error (_ "bad arguments to " ++ "resize") { resize_op = Operator "resize" resize_im Operator_type.COMPOUND_REWRAP false; xfac' = to_real xfac; yfac' = to_real yfac; rxfac' = 1 / xfac'; ryfac' = 1 / yfac'; is_nn = kernel.type == Kernel_type.NEAREST_NEIGHBOUR; resize_im im // upscale by integer factor, nearest neighbour = im_zoom im xfac' yfac', is_int xfac' && is_int yfac' && xfac' >= 1 && yfac' >= 1 && is_nn // downscale by integer factor, nearest neighbour = im_subsample im rxfac' ryfac', is_int rxfac' && is_int ryfac' && rxfac' >= 1 && ryfac' >= 1 && is_nn // everything else ... we just pass on to vips_resize() = vips_resize kernel xfac' yfac' im { vips_resize kernel hscale vscale im = out { [out] = vips_call "resize" [im, hscale] [$vscale => vscale, $kernel => kernel.value]; } } } sharpen radius x1 y2 y3 m1 m2 in = oo_unary_function sharpen_op in, is_class in = im_sharpen in (to_real radius) (to_real x1) (to_real y2) (to_real y3) (to_real m1) (to_real m2), is_image in = error (_ "bad arguments to " ++ "sharpen") { sharpen_op = Operator "sharpen" (sharpen radius x1 y2 y3 m1 m2) Operator_type.COMPOUND_REWRAP false; } tone_analyse s m h sa ma ha in = oo_unary_function tone_analyse_op in, is_class in = im_tone_analyse in (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha), is_image in = error (_ "bad arguments to " ++ "tone_analyse") { tone_analyse_op = Operator "tone_analyse" (Plot_histogram @ tone_analyse s m h sa ma ha) Operator_type.COMPOUND false; } tone_map hist image = oo_binary_function tone_map_op hist image, is_class hist = oo_binary'_function tone_map_op hist image, is_class image = im_tone_map image hist, is_image hist && is_image image = error (_ "bad arguments to " ++ "tone_map") { tone_map_op = Operator "tone_map" tone_map Operator_type.COMPOUND_REWRAP false; } tone_build fmt b w s m h sa ma ha = (Plot_histogram @ clip2fmt fmt) (im_tone_build_range mx mx (to_real b) (to_real w) (to_real s) (to_real m) (to_real h) (to_real sa) (to_real ma) (to_real ha)) { mx = Image_format.maxval fmt; } icc_export depth profile intent in = oo_unary_function icc_export_op in, is_class in = im_icc_export_depth in (to_real depth) (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_export") { icc_export_op = Operator "icc_export" (icc_export depth profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import profile intent in = oo_unary_function icc_import_op in, is_class in = im_icc_import in (expand profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import") { icc_import_op = Operator "icc_import" (icc_import profile intent) Operator_type.COMPOUND_REWRAP false; } icc_import_embedded intent in = oo_unary_function icc_import_embedded_op in, is_class in = im_icc_import_embedded in (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_import_embedded") { icc_import_embedded_op = Operator "icc_import_embedded" (icc_import_embedded intent) Operator_type.COMPOUND_REWRAP false; } icc_transform in_profile out_profile intent in = oo_unary_function icc_transform_op in, is_class in = im_icc_transform in (expand in_profile) (expand out_profile) (to_real intent), is_image in = error (_ "bad arguments to " ++ "icc_transform") { icc_transform_op = Operator "icc_transform" (icc_transform in_profile out_profile intent) Operator_type.COMPOUND_REWRAP false; } icc_ac2rc profile in = oo_unary_function icc_ac2rc_op in, is_class in = im_icc_ac2rc in (expand profile), is_image in = error (_ "bad arguments to " ++ "icc_ac2rc") { icc_ac2rc_op = Operator "icc_ac2rc" (icc_ac2rc profile) Operator_type.COMPOUND_REWRAP false; } // Given a xywh rect, flip it around so wh are always positive rect_normalise x y w h = [x', y', w', h'] { x' = x + w, w < 0 = x; y' = y + h, h < 0 = y; w' = abs w; h' = abs h; } draw_flood_blob x y ink image = oo_unary_function draw_flood_blob_op image, is_class image = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood_blob") { draw_flood_blob_op = Operator "draw_flood_blob" (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; } draw_flood x y ink image = oo_unary_function draw_flood_op image, is_class image = im_draw_flood image (to_real x) (to_real y) ink, is_image image = error (_ "bad arguments to " ++ "draw_flood") { draw_flood_op = Operator "draw_flood" (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; } /* This version of draw_rect uses insert_noexpand and will be fast, even for * huge images. */ draw_rect_width x y w h f t ink image = oo_unary_function draw_rect_width_op image, is_class image = my_draw_rect_width image (to_int x) (to_int y) (to_int w) (to_int h) (to_int f) (to_int t) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_width") { draw_rect_width_op = Operator "draw_rect_width" (draw_rect_width x y w h f t ink) Operator_type.COMPOUND_REWRAP false; my_draw_rect_width image x y w h f t ink = insert x' y' (block w' h') image, f == 1 = (insert x' y' (block w' t) @ insert (x' + w' - t) y' (block t h') @ insert x' (y' + h' - t) (block w' t) @ insert x' y' (block t h')) image { insert = insert_noexpand; block w h = image_new w h (get_bands image) (get_format image) (get_coding image) (get_type image) ink' 0 0; ink' = Vector ink, is_list ink = ink; [x', y', w', h'] = rect_normalise x y w h; } } /* Default to 1 pixel wide edges. */ draw_rect x y w h f ink image = draw_rect_width x y w h f 1 ink image; /* This version of draw_rect uses the paintbox rect draw operation. It is an * inplace operation and will use bucketloads of memory. */ draw_rect_paintbox x y w h f ink image = oo_unary_function draw_rect_op image, is_class image = im_draw_rect image (to_real x) (to_real y) (to_real w) (to_real h) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_rect_paintbox") { draw_rect_op = Operator "draw_rect" (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; } draw_circle x y r f ink image = oo_unary_function draw_circle_op image, is_class image = im_draw_circle image (to_real x) (to_real y) (to_real r) (to_real f) ink, is_image image = error (_ "bad arguments to " ++ "draw_circle") { draw_circle_op = Operator "draw_circle" (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; } draw_line x1 y1 x2 y2 ink image = oo_unary_function draw_line_op image, is_class image = im_draw_line image (to_real x1) (to_real y1) (to_real x2) (to_real y2) ink, is_image image = error (_ "bad arguments to " ++ "draw_line") { draw_line_op = Operator "draw_line" (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; } print_base base in = oo_unary_function print_base_op in, is_class in = map (print_base base) in, is_list in = print_base_real, is_real in = error (_ "bad arguments to " ++ "print_base") { print_base_op = Operator "print_base" (print_base base) Operator_type.COMPOUND false; print_base_real = error "print_base: bad base", base < 2 || base > 16 = "0", in < 0 || chars == [] = reverse chars { digits = map (\x x % base) (takewhile (not_equal 0) (iterate (\x idivide x base) in)); chars = map tohd digits; tohd x = (char) ((int) '0' + x), x < 10 = (char) ((int) 'A' + (x - 10)); } } /* id x: the identity function * * id :: * -> * */ id x = x; /* const x y: junk y, return x * * (const 3) is the function that always returns 3. * const :: * -> ** -> * */ const x y = x; /* converse fn a b: swap order of args to fn * * converse fn a b == fn b a * converse :: (* -> ** -> ***) -> ** -> * -> *** */ converse fn a b = fn b a; /* fix fn x: find the fixed point of a function */ fix fn x = limit (iterate fn x); /* until pred fn n: apply fn to n until pred succeeds; return that value * * until (more 1000) (multiply 2) 1 = 1024 * until :: (* -> bool) -> (* -> *) -> * -> * */ until pred fn n = n, pred n = until pred fn (fn n); /* Infinite list of primes. */ primes = 1 : (sieve [2 ..]) { sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); nmultiple n x = x / n != (int) (x / n); } /* Map an n-ary function (pass the args as a list) over groups of objects. * The objects can be single objects or groups. If more than one * object is a group, we iterate for the length of the smallest group. * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the * output and don't apply the function. copy-pasted into _types, keep in sync */ map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { // find all the group arguments groups = filter is_Group args; // what's the length of the shortest group arg? shortest = foldr1 min_pair (map (len @ get_value) groups); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested groups too process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } /* Map a 1-ary function over an object. */ map_unary fn a = map_nary (list_1ary fn) [a]; /* Map a 2-ary function over a pair of objects. */ map_binary fn a b = map_nary (list_2ary fn) [a, b]; /* Map a 3-ary function over three objects. */ map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; /* Map a 4-ary function over three objects. */ map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; /* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on * vectors and matricies. */ map_nary_list fn args = fn args, lists == [] = map process [0, 1 .. shortest - 1] { // find all the list arguments lists = filter is_list args; // what's the length of the shortest list arg? shortest = foldr1 min_pair (map len lists); // process index n ... pull that member from each argument // recurse to handle application, so we work for nested lists too process n = map_nary_list fn (map (extract n) args) { extract n arg = arg?n, is_list arg = arg; } } map_unaryl fn a = map_nary_list (list_1ary fn) [a]; map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; /* Remove features smaller than x pixels across from an image. This used to be * rather complex ... convsep is now good enough to use. */ smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; /* Chop up an image into a list of lists of smaller images. Pad edges with * black. */ imagearray_chop tile_width tile_height hoverlap voverlap i = map chop' [0, vstep .. last_y] { width = get_width i; height = get_height i; bands = get_bands i; format = get_format i; type = get_type i; tile_width' = to_real tile_width; tile_height' = to_real tile_height; hoverlap' = to_real hoverlap; voverlap' = to_real voverlap; /* Unique pixels per tile. */ hstep = tile_width' - hoverlap'; vstep = tile_height' - voverlap'; /* Number of tiles across and down. Remember the case where width == * hstep. */ across = (int) ((width - 1) / hstep) + 1; down = (int) ((height - 1) / vstep) + 1; /* top/left of final tile. */ last_x = hstep * (across - 1); last_y = vstep * (down - 1); /* How much do we need to pad by? */ sx = last_x + tile_width'; sy = last_y + tile_height'; /* Expand image with black to pad size. */ pad = embed 0 0 0 sx sy i; /* Chop up a row. */ chop' y = map chop'' [0, hstep .. last_x] { chop'' x = extract_area x y tile_width' tile_height' pad; } } /* Reassemble image. */ imagearray_assemble hoverlap voverlap il = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il { lrj l r = insert (get_width l + hoverlap) 0 r l; tbj t b = insert 0 (get_height t + voverlap) b t; } /* Generate an nxn identity matrix. */ identity_matrix n = error "identity_matrix: n > 0", n < 1 = map line [0 .. n - 1] { line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; } /* Incomplete gamma function Q(a, x) == 1 - P(a, x) FIXME ... this is now a builtin, until we can get a nice List class (requires overloadable ':') gammq a x = error "bad args", x < 0 || a <= 0 = 1 - gamser, x < a + 1 = gammcf { gamser = (gser a x)?0; gammcf = (gcf a x)?0; } */ /* Incomplete gamma function P(a, x) evaluated as series representation. Also * return ln(gamma(a)) ... nr in c, pp 218 */ gser a x = [gamser, gln] { gln = gammln a; gamser = error "bad args", x < 0 = 0, x == 0 = 1 // fix this { // maximum iterations maxit = 100; ap = List [a + 1, a + 2 ...]; xoap = x / ap; del = map product (prefixes xoap.value); /* del = map (multiply (1 / a)) (map product (prefixes xoap)) del = xap = iterate (multiply */ /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, * 3], [1, 2, 3, 4] ...] */ prefixes l = map (converse take l) [1..]; } } /* ln(gamma(xx)) ... nr in c, pp 214 */ gammln xx = gln { cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; y = take 6 (iterate (add 1) (xx + 1)); ser = 1.000000000190015 + sum (map2 divide cof y); tmp = xx + 0.5; tmp' = tmp - ((xx + 0.5) * log tmp); gln = -tmp + log (2.5066282746310005 * ser / xx); } /* make a LUT from a scatter */ buildlut x = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 = error (_ "bad arguments to " ++ "buildlut"); /* Linear regression. Return a class with the stuff we need in. * from s15.2, p 665 NR in C * * Also calculate R2, see eg.: * https://en.wikipedia.org/wiki/Coefficient_of_determination */ linreg xes yes = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; siga = (chi2 / (ss - 2)) ** 0.5 * ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; // for compat with linregw, see below q = 1.0; R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } ss = len xes; sx = sum xes; sy = sum yes; my = sy / ss; sxoss = sx / ss; tes = [x - sxoss :: x <- xes]; st2 = sum [t ** 2 :: t <- tes]; } /* Weighted linear regression. Xes, yes and a list of deviations. */ linregw xes yes devs = obj { obj = class { // in case we ever get shown in the workspace _vislevel = 2; slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; intercept = (sy - sx * slope) / ss; chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: [x, y, sd] <- zip3 xes yes devs]; siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; sigb = (1 / st2) ** 0.5; q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); R2 = 1 - chi2 / sum [(y - my) ** 2 :: y <- yes]; } wt = [sd ** -0.5 :: sd <- devs]; ss = sum wt; sx = sum [x * w :: [x, w] <- zip2 xes wt]; sy = sum [y * w :: [y, w] <- zip2 yes wt]; my = sy / len xes; sxoss = sx / ss; tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; st2 = sum [t ** 2 :: t <- tes]; } /* Clustering: pass in a list of points, repeatedly merge the * closest two points until no two points are closer than the threshold. * Return [merged-points, corresponding-weights]. A weight is a list of the * indexes we merged to make that point, ie. len weight == how significant * this point is. * * eg. * cluster 12 [152,154,155,42,159] == * [[155,42],[[1,2,0,4],[3]]] */ cluster thresh points = oo_unary_function cluster_op points, is_class points // can't use [0..len points - 1], in case len points == 0 = merge [points, map (converse cons []) (take (len points) [0 ..])], is_list points = error (_ "bad arguments to " ++ "cluster") { cluster_op = Operator "cluster" (cluster thresh) Operator_type.COMPOUND false; merge x = x, m < 2 || d > thresh = merge [points', weights'] { [points, weights] = x; m = len points; // generate indexes of all possible pairs, avoiding comparing a thing // to itself, and assuming that dist is reflexive // first index is always less than 2nd index // the +1,+2 makes sure we have an increasing generator, otherwise we // can get [3 .. 4] (for example), which will make a decreasing // sequence pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; // distance function // arg is eg. [3,1], meaning get distance from point 3 to point 1 dist x = abs (points?i - points?j) { [i, j] = x; } // smallest distance, then the two points we merge p = minpos (map dist pairs); d = dist pairs?p; [i, j] = pairs?p; // new point and new weight nw = weights?i ++ weights?j; np = (points?i * len weights?i + points?j * len weights?j) / len nw; // remove element i from a list remove i l = take i l ++ drop (i + 1) l; // remove two old points, add the new merged one // i < j (see "pairs", above) points' = np : remove i (remove j points); weights' = nw : remove i (remove j weights); } } /* Extract the area of an image around an arrow. * Transform the image to make the arrow horizontal, then displace by hd and * vd pxels, then cut out a bit h pixels high centered on the arrow. */ extract_arrow hd vd h arrow = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' { // the line as a polar vector pv = polar (arrow.width, arrow.height); a = im pv; // smallest rotation that will make the line horizontal a' = 360 - a, a > 270 = 180 - a, a > 90 = -a; im' = rotate Interpolate_bilinear a' arrow.image; // look at the start and end of the arrow, pick the leftmost p = (arrow.left, arrow.top), arrow.left <= arrow.right = (arrow.right, arrow.bottom); // transform that point to im' space p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); } /* You'd think these would go in _convert, but they are not really colour ops, * so put them here. */ rad2float image = oo_unary_function rad2float_op image, is_class image = im_rad2float image, is_image image = error (_ "bad arguments to " ++ "rad2float") { rad2float_op = Operator "rad2float" rad2float Operator_type.COMPOUND_REWRAP false; } float2rad image = oo_unary_function float2rad_op image, is_class image = im_float2rad image, is_image image = error (_ "bad arguments to " ++ "float2rad") { float2rad_op = Operator "float2rad" float2rad Operator_type.COMPOUND_REWRAP false; } segment x = oo_unary_function segment_op x, is_class x = image', is_image x = error (_ "bad arguments to " ++ "segment") { segment_op = Operator "segment" segment Operator_type.COMPOUND_REWRAP false; [image, nsegs] = im_segment x; image' = im_copy_set_meta image "n-segments" nsegs; } point a b = oo_binary_function point_op a b, is_class a = oo_binary'_function point_op a b, is_class b = im_read_point b x y, is_image b = [b?x?y], is_matrix b = [b?x], is_real_list b && y == 0 = [b?y], is_real_list b && x == 0 = error (_ "bad arguments to " ++ "point") { point_op = Operator "point" (\a\b Vector (point a b)) Operator_type.COMPOUND false; (x, y) = a, is_complex a; = (a?0, a?1), is_real_list a && is_list_len 2 a = error "bad position format"; } /* One image in, one out. */ system_image command x = oo_unary_function system_image_op x, is_class x = system x, is_image x = error (_ "bad arguments to " ++ "system_image") { system_image_op = Operator "system_image" (system_image command) Operator_type.COMPOUND_REWRAP false; system im = image_out { [image_out, log] = im_system_image (get_image im) "%s.tif" "%s.tif" command; } } /* Two images in, one out. */ system_image2 command x1 x2 = oo_binary_function system_image2_op x1 x2, is_class x1 = oo_binary'_function system_image2_op x1 x2, is_class x2 = system x1 x2, is_image x1 && is_image x2 = error (_ "bad arguments to " ++ "system_image2") { system_image2_op = Operator "system_image2" (system_image2 command) Operator_type.COMPOUND_REWRAP false; system x1 x2 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Three images in, one out. */ system_image3 command x1 x2 x3 = oo_binary_function system_image2_op x2 x3, is_class x2 = oo_binary'_function system_image2_op x2 x3, is_class x3 = system x1 x2 x3, is_image x1 && is_image x2 && is_image x3 = error (_ "bad arguments to " ++ "system_image3") { system_image2_op = Operator "system_image2" (system_image3 command x1) Operator_type.COMPOUND_REWRAP false; system x1 x2 x3 = image_out { [image_out] = vips_call "system" [command] [ $in => [x1, x2, x3], $out => true, $out_format => "%s.tif", $in_format => "%s.tif" ]; } } /* Zero images in, one out. */ system_image0 command = Image image_out { [image_out] = vips_call "system" [command] [ $out => true, $out_format => "%s.tif" ]; } hough_line w h x = oo_unary_function hough_line_op x, is_class x = hline (to_real w) (to_real h) x, is_image x = error (_ "bad arguments to " ++ "hough_line") { hough_line_op = Operator "hough_line" (hough_line w h) Operator_type.COMPOUND_REWRAP false; hline w h x = pspace { [pspace] = vips_call "hough_line" [x] [ $width => w, $height => h ]; } } hough_circle s mn mx x = oo_unary_function hough_circle_op x, is_class x = hcircle (to_real s) (to_real mn) (to_real mx) x, is_image x = error (_ "bad arguments to " ++ "hough_circle") { hough_circle_op = Operator "hough_circle" (hough_circle s mn mx) Operator_type.COMPOUND_REWRAP false; hcircle s mn mx x = pspace { [pspace] = vips_call "hough_circle" [x] [ $scale => s, $min_radius => mn, $max_radius => mx ]; } } mapim interp ind in = oo_binary_function mapim_op ind in, is_class ind = oo_binary'_function mapim_op ind in, is_class in = mapim_fn ind in, is_image ind && is_image in = error (_ "bad arguments to " ++ "mapim") { mapim_op = Operator "mapim" (mapim interp) Operator_type.COMPOUND_REWRAP false; mapim_fn ind im = out { [out] = vips_call "mapim" [im, ind] [$interpolate => interp]; } } perlin cell width height = Image im { [im] = vips_call "perlin" [to_real width, to_real height] [ $cell_size => to_real cell ]; } worley cell width height = Image im { [im] = vips_call "worley" [to_real width, to_real height] [ $cell_size => to_real cell ]; } gaussnoise width height mean sigma = im { [im] = vips_call "gaussnoise" [to_real width, to_real height] [ $mean => to_real mean, $sigma => to_real sigma ]; } flattenimage bg x = oo_unary_function flatten_op x, is_class x = flt (to_vector bg) x, is_image x = error (_ "bad arguments to " ++ "flattenimage") { flatten_op = Operator "flatten" (flattenimage bg) Operator_type.COMPOUND_REWRAP false; flt bg x = out { [out] = vips_call "flatten" [x] [ $background => bg.value ]; } } premultiply x = oo_unary_function premultiply_op x, is_class x = prem x, is_image x = error (_ "bad arguments to " ++ "premultiply") { premultiply_op = Operator "premultiply" premultiply Operator_type.COMPOUND_REWRAP false; prem x = out { [out] = vips_call "premultiply" [x] [ ]; } } unpremultiply x = oo_unary_function unpremultiply_op x, is_class x = unprem x, is_image x = error (_ "bad arguments to " ++ "unpremultiply") { unpremultiply_op = Operator "unpremultiply" unpremultiply Operator_type.COMPOUND_REWRAP false; unprem x = out { [out] = vips_call "unpremultiply" [x] [ ]; } } hist_entropy x = oo_unary_function hist_entropy_op x, is_class x = entropy x, is_image x = error (_ "bad arguments to " ++ "hist_entropy") { hist_entropy_op = Operator "hist_entropy" hist_entropy Operator_type.COMPOUND_REWRAP false; entropy x = out { [out] = vips_call "hist_entropy" [x] [ ]; } } canny sigma precision x = oo_unary_function canny_op x, is_class x = canny_im (to_real sigma) (to_int precision) x, is_image x = error (_ "bad arguments to " ++ "canny") { canny_op = Operator "canny" (canny sigma precision) Operator_type.COMPOUND_REWRAP false; canny_im sigma precision x = out { [out] = vips_call "canny" [x] [ $sigma => sigma, $precision => precision ]; } } sobel x = oo_unary_function sobel_op x, is_class x = sobel_im x, is_image x = error (_ "bad arguments to " ++ "sobel") { sobel_op = Operator "sobel" sobel Operator_type.COMPOUND_REWRAP false; sobel_im x = out { [out] = vips_call "sobel" [x] [ ]; } } ================================================ FILE: share/nip2/start/_types.def ================================================ /* A list of things. Do automatic iteration of unary and binary operators on * us. * List [1, 2] + [2, 3] -> List [3, 5] * hd (List [2, 3]) -> 2 * List [] == [] -> true */ List value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ [apply2 op value x', op.op_name == "subscript" || op.op_name == "subscript'" || op.op_name == "equal" || op.op_name == "equal'"], [this.List (apply2 op value x'), op.op_name == "join" || op.op_name == "join'"], [this.List (map2 (apply2 op) value x'), is_list x'], [this.List (map (apply2 op' x) value), true] ] ++ super.oo_binary_table op x { op' = oo_converse op; // strip the List wrapper, if any x' = x.value, is_List x = x; apply2 op x1 x2 = oo_binary_function op x1 x2, is_class x1 = oo_binary'_function op x1 x2, is_class x2 = op.fn x1 x2; }; oo_unary_table op = [ [apply value, op.op_name == "hd" || op.op_name == "tl"], [this.List (map apply value), true] ] ++ super.oo_unary_table op { apply x = oo_unary_function op x, is_class x = op.fn x; } } /* A group of things. Loop the operation over the group. */ Group value = class _Object { _check_args = [ [value, "value", check_list] ]; // methods oo_binary_table op x = [ // if_then_else is really a trinary operator [map_trinary ite this x?0 x?1, op.op_name == "if_then_else"], [map_binary op.fn this x, is_Group x], [map_unary (\a op.fn a x) this, true] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [map_unary op.fn this, true] ] ++ super.oo_unary_table op; // we can't call map_trinary directly, since it uses Group and we // don't support mutually recursive top-level functions :-( // copy-paste it here, keep in sync with the version in _stdenv map_nary fn args = fn args, groups == [] = Group (map process [0, 1 .. shortest - 1]) { groups = filter is_Group args; shortest = foldr1 min_pair (map (len @ get_value) groups); process n = NULL, any (map (is_noval n) args) = map_nary fn (map (extract n) args) { extract n arg = arg.value?n, is_Group arg = arg; is_noval n arg = is_Group arg && arg.value?n == NULL; } } // need ite as a true trinary ite a b c = if a then b else c; map_unary fn a = map_nary (list_1ary fn) [a]; map_binary fn a b = map_nary (list_2ary fn) [a, b]; map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; } /* Single real number ... eg slider. */ Real value = class _Object { _check_args = [ [value, "value", check_real] ]; // methods oo_binary_table op x = [ [this.Real (op.fn this.value x.value), is_Real x && op.type == Operator_type.ARITHMETIC], [this.Real (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], [op.fn this.value x.value, is_Real x && op.type == Operator_type.RELATIONAL], [op.fn this.value x, !is_class x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Real (op.fn this.value), op.type == Operator_type.ARITHMETIC], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* Single bool ... eg Toggle. */ Bool value = class _Object { _check_args = [ [value, "value", check_bool] ]; // methods oo_binary_table op x = [ [op.fn this.value x, op.op_name == "if_then_else"], [this.Bool (op.fn this.value x.value), is_Bool x], [this.Bool (op.fn this.value x), is_bool x] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [this.Bool (op.fn this.value), op.type == Operator_type.ARITHMETIC || op.type == Operator_type.RELATIONAL], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* An editable string. */ String caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable real number. */ Number caption value = class scope.Real value { _check_args = [ [caption, "caption", check_string] ]; Real x = this.Number caption x; } /* An editable expression. */ Expression caption expr = class (if is_class expr then expr else _Object) { _check_args = [ [caption, "caption", check_string], [expr, "expr", check_any] ]; } /* A ticking clock. */ Clock interval value = class scope.Real value { _check_args = [ [interval, "interval", check_real] ]; Real x = this.Clock interval x; } /* An editable filename. */ Pathname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* An editable fontname. */ Fontname caption value = class _Object { _check_args = [ [caption, "caption", check_string], [value, "value", check_string] ]; } /* Vector type ... just a finite list of real. Handy for wrapping an * argument to eg. im_lintra_vec. Make it behave like a single pixel image. */ Vector value = class _Object { _check_args = [ [value, "value", check_real_list] ]; bands = len value; // methods oo_binary_table op x = [ // Vector ++ Vector means bandwise join [this.Vector (op.fn this.value x.value), is_Vector x && (op.op_name == "join" || op.op_name == "join'")], [this.Vector (op.fn this.value [get_number x]), has_number x && (op.op_name == "join" || op.op_name == "join'")], // Vector ? number means extract element [op.fn this.value (get_real x), has_real x && (op.op_name == "subscript" || op.op_name == "subscript'")], // extra check for lengths equal [this.Vector (map_binaryl op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.ARITHMETIC], [this.Vector (map_binaryl op.fn this.value (get_real x)), has_real x && op.type == Operator_type.ARITHMETIC], // need extra length check [this.Vector (map bool_to_real (map_binaryl op.fn this.value x.value)), is_Vector x && len value == len x.value && op.type == Operator_type.RELATIONAL], [this.Vector (map bool_to_real (map_binaryl op.fn this.value (get_real x))), has_real x && op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value x.value), is_Vector x && len value == len x.value && op.type == Operator_type.COMPOUND_REWRAP], [x.Image (vec op'.op_name x.value value), is_Image x], [vec op'.op_name x value, is_image x], [op.fn this.value x, is_real x] ] ++ super.oo_binary_table op x { op' = oo_converse op; }; oo_unary_table op = [ [this.Vector (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Vector (map bool_to_real (map_unaryl op.fn this.value)), op.type == Operator_type.RELATIONAL], [this.Vector (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; // turn an ip bool (or a number, for Vector) into VIPSs 255/0 bool_to_real x = 255, is_bool x && x = 255, is_number x && x != 0 = 0; } /* A rectangular array of real. */ Matrix_base value = class _Object { _check_args = [ [value, "value", check_matrix] ]; // calculate these from value width = len value?0; height = len value; // extract a rectanguar area extract left top width height = this.Matrix_base ((map (take width) @ map (drop left) @ take height @ drop top) value); // methods oo_binary_table op x = [ // mat multiply is special [this.Matrix_base mul.value, is_Matrix x && op.op_name == "multiply"], [this.Matrix_base mul'.value, is_Matrix x && op.op_name == "multiply'"], // mat divide is also special [this.Matrix_base div.value, is_Matrix x && op.op_name == "divide"], [this.Matrix_base div'.value, is_Matrix x && op.op_name == "divide'"], // power -1 means invert [this.Matrix_base inv.value, is_real x && x == -1 && op.op_name == "power"], [this.Matrix_base sq.value, is_real x && x == 2 && op.op_name == "power"], [error "matrix **-1 and **2 only", op.op_name == "power" || op.op_name == "power'"], // matrix op vector ... treat a vector as a 1 row matrix [this.Matrix_base (map (map_binaryl op'.fn x.value) this.value), is_Vector x && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x.value), (is_Matrix x || is_Real x) && op.type == Operator_type.ARITHMETIC], [this.Matrix_base (map_binaryl op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC], // compound ... don't do iteration [this.Matrix_base (op.fn this.value x.value), (is_Matrix x || is_Real x || is_Vector x) && op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value x, op.type == Operator_type.COMPOUND] ] ++ super.oo_binary_table op x { mul = im_matmul this x; mul' = im_matmul x this; div = im_matmul this (im_matinv x); div' = im_matmul x (im_matinv this); inv = im_matinv this; sq = im_matmul this this; op' = oo_converse op; } oo_unary_table op = [ [this.Matrix_base (map_unaryl op.fn this.value), op.type == Operator_type.ARITHMETIC], [this.Matrix_base (op.fn this.value), op.type == Operator_type.COMPOUND_REWRAP], [op.fn this.value, true] ] ++ super.oo_unary_table op; } /* How to display a matrix: text, sliders, toggles, or text plus scale/offset. */ Matrix_display = class { text = 0; slider = 1; toggle = 2; text_scale_offset = 3; is_display = member [text, slider, toggle, text_scale_offset]; } /* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add * a display type as well to control how the widget renders. */ Matrix_vips value scale offset filename display = class scope.Matrix_base value { _check_args = [ [scale, "scale", check_real], [offset, "offset", check_real], [filename, "filename", check_string], [display, "display", check_matrix_display] ]; Matrix_base x = this.Matrix_vips x scale offset filename display; } /* A plain 'ol matrix which can be passed to VIPS. */ Matrix value = class Matrix_vips value 1 0 "" Matrix_display.text {} /* Specialised constructors ... for convolutions, recombinations and * morphologies. */ Matrix_con scale offset value = class Matrix_vips value scale offset "" Matrix_display.text_scale_offset {}; Matrix_rec value = class Matrix_vips value 1 0 "" Matrix_display.slider {}; Matrix_mor value = class Matrix_vips value 1 0 "" Matrix_display.toggle {}; Matrix_file filename = (im_read_dmask @ expand @ search) filename; /* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc) */ Colour colour_space value = class scope.Vector value { _check_args = [ [colour_space, "colour_space", check_colour_space] ]; _check_all = [ [is_list_len 3 value, "len value == 3"] ]; Vector x = this.Colour colour_space x; // make a colour-ish thing from an image // back to Colour if we have another 3 band image // to a vector if bands > 1 // to a number otherwise itoc im = this.Colour nip_type (to_matrix im).value?0, bands == 3 = scope.Vector (map mean (bandsplit im)), bands > 1 = mean im { type = get_header "Type" im; bands = get_header "Bands" im; nip_type = Image_type.colour_spaces.lookup 1 0 type; } // methods oo_binary_table op x = [ [itoc (op.fn ((float) (to_image this).value) ((float) (to_image x).value)), // here REWRAP means go via image op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_binary_table op x; oo_unary_table op = [ [itoc (op.fn ((float) (to_image this).value)), op.type == Operator_type.COMPOUND_REWRAP] ] ++ super.oo_unary_table op; } // a subclass with widgets for picking a space and value Colour_picker default_colour default_value = class Colour space.item colour.expr { _vislevel = 3; space = Option_enum "Colour space" Image_type.colour_spaces default_colour; colour = Expression "Colour value" default_value; Colour_edit colour_space value = Colour_picker colour_space value; } /* Base scale type. */ Scale caption from to value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [from, "from", check_real], [to, "to", check_real] ]; _check_all = [ [from < to, "from < to"] ]; Real x = this.Scale caption from to x; // methods oo_binary_table op x = [ [this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to) (op.fn this.value x.value), is_Scale x && op.type == Operator_type.ARITHMETIC], [this.Scale caption (op.fn this.from x) (op.fn this.to x) (op.fn this.value x), is_real x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x; } /* Base toggle type. */ Toggle caption value = class scope.Bool value { _check_args = [ [caption, "caption", check_string], [value, "value", check_bool] ]; Bool x = this.Toggle caption x; } /* Base option type. */ Option caption labels value = class scope.Real value { _check_args = [ [caption, "caption", check_string], [labels, "labels", check_string_list], [value, "value", check_uint] ]; } /* An option whose value is a string rather than a number. */ Option_string caption labels item = class Option caption labels (index (equal item) labels) { Option_edit caption labels value = this.Option_string caption labels (labels?value); } /* Make an option from an enum. */ Option_enum caption enum item = class Option_string caption enum.names item { // corresponding thing value_thing = enum.get_thing item; Option_edit caption labels value = this.Option_enum caption enum (enum.names?value); } /* A rectangle. width and height can be -ve. */ Rect left top width height = class _Object { _check_args = [ [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; // derived right = left + width; bottom = top + height; oo_binary_table op x = [ [equal x, is_Rect x && (op.op_name == "equal" || op.op_name == "equal'")], [!equal x, is_Rect x && (op.op_name == "not_equal" || op.op_name == "not_equal'")], // binops with a complex are the same as (comp op comp) [oo_binary_function op this (Rect (re x) (im x) 0 0), is_complex x], // all others are just pairwise [this.Rect left' top' width' height', is_Rect x && op.type == Operator_type.ARITHMETIC], [this.Rect left'' top'' width'' height'', has_number x && op.type == Operator_type.ARITHMETIC] ] ++ super.oo_binary_table op x { left' = op.fn left x.left; top' = op.fn top x.top; width' = op.fn width x.width; height' = op.fn height x.height; left'' = op.fn left x'; top'' = op.fn top x'; width'' = op.fn width x'; height'' = op.fn height x'; x' = get_number x; } oo_unary_table op = [ // arithmetic uops just map [this.Rect left' top' width' height', op.type == Operator_type.ARITHMETIC], // compound uops are just like ops on complex // do (width, height) so thing like abs(Arrow) work as you'd expect [op.fn (width, height), op.type == Operator_type.COMPOUND] ] ++ super.oo_unary_table op { left' = op.fn left; top' = op.fn top; width' = op.fn width; height' = op.fn height; } // empty? ie. contains no pixels is_empty = width == 0 || height == 0; // normalised version, ie. make width/height +ve and flip the origin nleft = left + width, width < 0 = left; ntop = top + height, height < 0 = top; nwidth = abs width; nheight = abs height; nright = nleft + nwidth; nbottom = ntop + nheight; equal x = left == x.left && top == x.top && width == x.width && height == x.height; // contains a point? includes_point x y = nleft <= x && x <= nright && ntop <= y && y <= nbottom; // contains a rect? just test top left and bottom right points includes_rect r = includes_point r.nleft r.ntop && includes_point r.nright r.nbottom; // bounding box of two rects // if either is empty, can just return the other union r = r, is_empty = this, r.is_empty = Rect left' top' width' height' { left' = min_pair nleft r.nleft; top' = min_pair ntop r.ntop; width' = max_pair nright r.nright - left'; height' = max_pair nbottom r.nbottom - top'; } // intersection of two rects ... empty rect if no intersection intersect r = Rect left' top' width'' height'' { left' = max_pair nleft r.nleft; top' = max_pair ntop r.ntop; width' = min_pair nright r.nright - left'; height' = min_pair nbottom r.nbottom - top'; width'' = width', width > 0 = 0; height'' = height', height > 0 = 0; } // expand/collapse by n pixels margin_adjust n = Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n); } /* Values for Compression field in image. */ Image_compression = class { NONE = 0; NO_COMPRESSION = 0; TCSF_COMPRESSION = 1; JPEG_COMPRESSION = 2; LABPACK_COMPRESSED = 3; RGB_COMPRESSED = 4; LUM_COMPRESSED = 5; } /* Values for Coding field in image. */ Image_coding = class { NONE = 0; NOCODING = 0; COLQUANT = 1; LABPACK = 2; RAD = 6; } /* Values for BandFmt field in image. */ Image_format = class { DPCOMPLEX = 9; DOUBLE = 8; COMPLEX = 7; FLOAT = 6; INT = 5; UINT = 4; SHORT = 3; USHORT = 2; CHAR = 1; UCHAR = 0; NOTSET = -1; maxval fmt = [ 255, // UCHAR 127, // CHAR 65535, // USHORT 32767, // SHORT 4294967295, // UINT 2147483647, // INT 255, // FLOAT 255, // COMPLEX 255, // DOUBLE 255 // DPCOMPLEX ] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX = error (_ "bad value for BandFmt"); } /* A lookup table. */ Table value = class _Object { _check_args = [ [value, "value", check_rectangular] ]; /* Extract a column. */ column n = map (extract n) value; /* present col x: is there an x in column col */ present col x = member (column col) x; /* Look on column from, return matching item in column to. */ lookup from to x = value?n?to, n >= 0 = error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table") { n = index (equal x) (column from); } } /* A two column lookup table with the first column a string and the second a * thing. Used for representing various enums. Option_enum makes a selector * from one of these. */ Enum value = class Table value { _check_args = [ [value, "value", check_enum] ] { check_enum = [is_enum, _ "is [[char, *]]"]; is_enum x = is_rectangular x && is_listof is_string (map (extract 0) x); } // handy ... all the names and things as lists names = this.column 0; things = this.column 1; // is a legal name or thing has_name x = this.present 1 x; has_thing x = this.present 0 x; // map things to strings and back get_name x = this.lookup 1 0 x; get_thing x = this.lookup 0 1 x; } /* Type field. */ Image_type = class { MULTIBAND = 0; B_W = 1; HISTOGRAM = 10; XYZ = 12; LAB = 13; CMYK = 15; LABQ = 16; RGB = 17; UCS = 18; LCH = 19; LABS = 21; sRGB = 22; YXY = 23; FOURIER = 24; RGB16 = 25; GREY16 = 26; ARRAY = 27; scRGB = 28; /* Table to get names <-> numbers. */ type_names = Enum [ $MULTIBAND => MULTIBAND, $B_W => B_W, $HISTOGRAM => HISTOGRAM, $XYZ => XYZ, $LAB => LAB, $CMYK => CMYK, $LABQ => LABQ, $RGB => RGB, $UCS => UCS, $LCH => LCH, $LABS => LABS, $sRGB => sRGB, $YXY => YXY, $FOURIER => FOURIER, $RGB16 => RGB16, $GREY16 => GREY16, $ARRAY => ARRAY, $scRGB => scRGB ]; /* Table relating nip's colour space names and VIPS's Type numbers. * Options are generated from this, so match the order to the order in * the Colour menu. */ colour_spaces = Enum [ $sRGB => sRGB, $scRGB => scRGB, $Lab => LAB, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; /* A slightly larger table ... the types of colorimetric image we can * have. Add mono, and the S and Q forms of LAB. */ image_colour_spaces = Enum [ $Mono => B_W, $sRGB => sRGB, $scRGB => scRGB, $RGB16 => RGB16, $GREY16 => GREY16, $Lab => LAB, $LabQ => LABQ, $LabS => LABS, $LCh => LCH, $XYZ => XYZ, $Yxy => YXY, $UCS => UCS ]; } /* Base image type. Simple layer over vips_image. */ Image value = class _Object { _check_args = [ [value, "value", check_image] ]; // fields from VIPS header width = get_width value; height = get_height value; bands = get_bands value; format = get_format value; bits = get_bits value; coding = get_coding value; type = get_type value; xres = get_header "Xres" value; yres = get_header "Yres" value; xoffset = get_header "Xoffset" value; yoffset = get_header "Yoffset" value; filename = get_header "filename" value; // convenience ... the area our pixels occupy, as a rect rect = Rect 0 0 width height; // operator overloading // (op Image Vector) done in Vector class oo_binary_table op x = [ // handle image ++ constant here [wrap join_result_image, (has_real x || is_Vector x) && (op.op_name == "join" || op.op_name == "join'")], [wrap ite_result_image, op.op_name == "if_then_else"], [wrap (op.fn this.value (get_image x)), has_image x], [wrap (op.fn this.value (get_number x)), has_number x], // if it's not a class on the RHS, handle here ... just apply and // rewrap [wrap (op.fn this.value x), !is_class x] // all other cases handled by other classes ] ++ super.oo_binary_table op x { // wrap the result with this // x can be a non-image, eg. compare "Image v == []" vs. // "Image v == 12" wrap x = x, op.type == Operator_type.COMPOUND || !is_image x = this.Image x; join_result_image = value ++ new_stuff, op.op_name == "join" = new_stuff ++ value { new_stuff = image_new width height new_bands format coding Image_type.B_W x xoffset yoffset; new_bands = get_bands x, has_bands x = 1; } [then_part, else_part] = x; // get things about our output from inputs in this order objects = [then_part, else_part, this]; // properties of our output image target_bands = get_member_list has_bands get_bands objects; target_type = get_member_list has_type get_type objects; // if one of then/else is an image, get the target format from that // otherwise, let the non-image objects set the target target_format = get_member_list has_format get_format x, has_member_list has_format x = NULL; to_image x = to_image_size width height target_bands target_format x; [then', else'] = map to_image x; ite_result_image = image_set_type target_type (if value then then' else else'); } // FIXME ... yuk ... don't use operator hints, just always rewrap if // we have an image result // forced on us by things like abs: // abs Vector -> real // abs Image -> Image // does not fit well with COMPOUND/whatever scheme oo_unary_table op = [ [this.Image result, is_image result], [result, true] ] ++ super.oo_unary_table op { result = op.fn this.value; } } /* Construct an image from a file. */ Image_file filename = class Image value { _check_args = [ [filename, "filename", check_string] ]; value = vips_image filename; } Region image left top width height = class Image value { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_preal], [height, "height", check_preal] ]; // a rect for our coordinates // region.rect gets the rect for the extracted image region_rect = Rect left top width height; // we need to always succeed ... value is our enclosing image if we're // out of bounds value = extract_area left top width height image.value, image.rect.includes_rect region_rect = image.value; } Area image left top width height = class scope.Region image left top width height { Region image left top width height = this.Area image left top width height; } Arrow image left top width height = class scope.Rect left top width height { _check_args = [ [image, "Image", check_Image], [left, "left", check_real], [top, "top", check_real], [width, "width", check_real], [height, "height", check_real] ]; Rect l t w h = this.Arrow image l t w h; } HGuide image top = class scope.Arrow image image.rect.left top image.width 0 { Arrow image left top width height = this.HGuide image top; } VGuide image left = class scope.Arrow image left image.rect.top 0 image.height { Arrow image left top width height = this.VGuide image left; } Mark image left top = class scope.Arrow image left top 0 0 { Arrow image left top width height = this.Mark image left top; } // convenience functions: ... specify position as [0 .. 1) Region_relative image u v w h = Region image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Area_relative image u v w h = Area image (image.width * u) (image.height * v) (image.width * w) (image.height * h); Arrow_relative image u v w h = Arrow image (image.width * u) (image.height * v) (image.width * w) (image.height * h); VGuide_relative image v = VGuide image (image.height * v); HGuide_relative image u = HGuide image (image.width * u); Mark_relative image u v = Mark image (image.width * u) (image.height * v); Kernel_type = class { NEAREST_NEIGHBOUR = 0; LINEAR = 1; CUBIC = 2; MITCHELL = 3; LANCZOS2 = 4; LANCZOS3 = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map kernel numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Linear", _ "Cubic", _ "Mitchell", _ "Lanczos, two lobes", _ "Lanczos, three lobes" ]; /* And to vips enum nicknames. */ types = [ "nearest", "linear", "cubic", "mitchell", "lanczos2", "lanczos3" ]; } Kernel type = class { value = Kernel_type.types?type; } Kernel_linear = Kernel Kernel_type.LINEAR; Kernel_picker default = class Kernel kernel.value { _vislevel = 2; kernel = Option "Kernel" Kernel_type.descriptions default; } Interpolate_type = class { NEAREST_NEIGHBOUR = 0; BILINEAR = 1; BICUBIC = 2; LBB = 3; NOHALO = 4; VSQBS = 5; // Should introspect to get the list of interpolators :-( // We can "dir" on VipsInterpolate to get a list of them, but we // can't get i18n'd descriptions until we have more // introspection stuff in nip2. /* Table to map interpol numbers to descriptive strings */ descriptions = [ _ "Nearest neighbour", _ "Bilinear", _ "Bicubic", _ "Upsize: reduced halo bicubic (LBB)", _ "Upsharp: reduced halo bicubic with edge sharpening (Nohalo)", _ "Upsmooth: quadratic B-splines with jaggy reduction (VSQBS)" ]; /* And to vips type names. */ types = [ "VipsInterpolateNearest", "VipsInterpolateBilinear", "VipsInterpolateBicubic", "VipsInterpolateLbb", "VipsInterpolateNohalo", "VipsInterpolateVsqbs" ]; } Interpolate type options = class { value = vips_object_new Interpolate_type.types?type [] options; } Interpolate_bilinear = Interpolate Interpolate_type.BILINEAR []; Interpolate_picker default = class Interpolate interp.value [] { _vislevel = 2; interp = Option "Interpolation" Interpolate_type.descriptions default; } Render_intent = class { PERCEPTUAL = 0; RELATIVE = 1; SATURATION = 2; ABSOLUTE = 3; /* Table to get names <-> numbers. */ names = Enum [ _ "Perceptual" => PERCEPTUAL, _ "Relative" => RELATIVE, _ "Saturation" => SATURATION, _ "Absolute" => ABSOLUTE ]; } // abstract base class for toolkit menus Menu = class {} // a "----" line in a menu Menuseparator = class Menu {} // abstract base class for items in menus Menuitem label tooltip = class Menu {} Menupullright label tooltip = class Menuitem label tooltip {} Menuaction label tooltip = class Menuitem label tooltip {} /* Plots. */ Plot_style = class { POINT = 0; LINE = 1; SPLINE = 2; BAR = 3; names = Enum [ _ "Point" => POINT, _ "Line" => LINE, _ "Spline" => SPLINE, _ "Bar" => BAR ]; } Plot_format = class { YYYY = 0; XYYY = 1; XYXY = 2; names = Enum [ _ "YYYY" => YYYY, _ "XYYY" => XYXY, _ "XYXY" => XYXY ]; } Plot_type = class { /* Lots of Ys (ie. multiple line plots). */ YYYY = 0; /* First column of matrix is X position, others are Ys (ie. multiple XY * line plots, all with the same Xes). */ XYYY = 1; /* Many independent XY plots. */ XYXY = 2; } /* "options" is a list of ["key", value] pairs. */ Plot options value = class scope.Image value { Image value = this.Plot options value; to_image dpi = extract_bands 0 3 (graph_export_image (to_real dpi) this); } Plot_matrix options value = class Plot options (to_image value).value { } Plot_histogram value = class scope.Plot [] value { } Plot_xy value = class scope.Plot [$format => Plot_format.XYYY] value { } /* A no-value type. Call it NULL for C-alike fun. Used by Group to indicate * empty slots, for example. */ NULL = class _Object { oo_binary_table op x = [ // the only operation we allow is equality .. use pointer equality, // this lets us test a == NULL and a != NULL [this === x, op.type == Operator_type.RELATIONAL && op.op_name == "equal"], [this !== x, op.type == Operator_type.RELATIONAL && op.op_name == "not_equal"] ] ++ super.oo_binary_table op x; } Blend_type = class { CLEAR = 0; SOURCE = 1; OVER = 2; IN = 3; OUT = 4; ATOP = 5; DEST = 6; DEST_OVER = 7; DEST_IN = 8; DEST_OUT = 9; DEST_ATOP = 10; XOR = 11; ADD = 12; SATURATE = 13; MULTIPLY = 14; SCREEN = 15; OVERLAY = 16; DARKEN = 17; LIGHTEN = 18; COLOUR_DODGE = 19; COLOUR_BURN = 20; HARD_LIGHT = 21; SOFT_LIGHT = 22; DIFFERENCE = 23; EXCLUSION = 24; /* Table to map blend numbers to descriptive strings */ descriptions = [ _ "Clear", _ "Source", _ "Over", _ "In", _ "Out", _ "Atop", _ "Dest", _ "Dest over", _ "Dest in", _ "Dest out", _ "Dest atop", _ "Xor", _ "Add", _ "Saturate", _ "Multiply", _ "Screen", _ "Overlay", _ "Darken", _ "Lighten", _ "Colour dodge", _ "Colour burn", _ "Hard light", _ "Soft light", _ "Difference", _ "Exclusion" ]; /* And to vips enum nicknames. */ types = Enum [ $clear => "clear", $source => "source", $over => "over", $in => "in", $out => "out", $atop => "atop", $dest => "dest", $dest_over => "dest_over", $dest_in => "dest_in", $dest_out => "dest_out", $dest_atop => "dest_atop", $xor => "xor", $add => "add", $saturate => "saturate", $multiply => "multiply", $screen => "screen", $overlay => "overlay", $darken => "darken", $lighten => "lighten", $colour_dodge => "colour_dodge", $colour_burn => "colour_burn", $hard_light => "hard_light", $soft_light => "soft_light", $difference => "difference", $exclusion => "exclusion" ]; } Blend type = class { value = Blend_type.types?type; } Blend_over = Blend Blend_type.OVER; Blend_picker default = class Blend blend.value { _vislevel = 2; blend = Option "Blend" Blend_type.descriptions default; } Combine_type = class { MAX = 0; SUM = 1; MIN = 2; enum = Enum [ _ "Maximum" => MAX, _ "Sum" => SUM, _ "Minimum" => MIN ]; } Combine type = class { value = Combine_type.enum.names?type; } Combine_sum = Combine Combine_type.SUM; Combine_picker default = Option "Combine" Combine_type.enum.names default; ================================================ FILE: src/BITMAPS/Makefile.am ================================================ EXTRA_DIST = \ ant.xbm \ automatic.xbm \ automatic.xpm \ automatic1.xbm \ automatic1.xpm \ automatic2.xbm \ automatic2.xpm \ automatic3.xbm \ automatic3.xpm \ change.xbm \ convol.xbm \ dropper.xpm \ dropper_msk.xbm \ dropper_src.xbm \ image.xbm \ kill.xbm \ mag_msk.xbm \ magin.xpm \ magin_src.xbm \ magout.xpm \ magout_src.xbm \ morph.xbm \ pan.xpm \ paint.xpm \ program.xbm \ select.xpm \ slider.xbm \ watch_1.xbm \ watch_2.xbm \ watch_3.xbm \ watch_4.xbm \ watch_5.xbm \ watch_6.xbm \ watch_7.xbm \ watch_8.xbm \ watch_msk.xbm \ book_open.xpm \ book_closed.xpm \ toolbox_open.xpm \ toolbox_closed.xpm \ tools.xpm \ col.xpm \ separator.xpm \ floppy.xpm \ mini_page.xpm ================================================ FILE: src/BITMAPS/ant.xbm ================================================ #define ant_width 64 #define ant_height 64 static unsigned char ant_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0x6a, 0x77, 0x77, 0xef, 0xfe, 0x5b, 0x55, 0x55, 0xb5, 0xad, 0xad, 0xb5, 0x6b, 0x17, 0xaa, 0x6a, 0x6b, 0xdb, 0x76, 0xdf, 0xde, 0x3d, 0xaa, 0x56, 0xd5, 0xb6, 0xad, 0xf5, 0x75, 0x57, 0x55, 0xd5, 0x2a, 0xd5, 0xf6, 0x5e, 0xdf, 0x2d, 0xaa, 0xad, 0xa4, 0x54, 0xad, 0xfb, 0x75, 0x3f, 0x55, 0x5b, 0x4a, 0x49, 0xdb, 0xae, 0xdf, 0x75, 0xaa, 0xaa, 0x28, 0x92, 0x74, 0xf7, 0xf6, 0x5f, 0xd5, 0x16, 0x45, 0x55, 0xad, 0xbd, 0x5b, 0x3b, 0xba, 0xaa, 0xa8, 0x88, 0xe8, 0xef, 0xfe, 0x2f, 0xa5, 0x45, 0x05, 0x25, 0x55, 0xbd, 0xab, 0x6a, 0x5d, 0x29, 0xaa, 0x48, 0xd2, 0xeb, 0xfe, 0x2d, 0x55, 0x42, 0x91, 0x92, 0x54, 0xbf, 0x95, 0x2a, 0xd5, 0x14, 0x24, 0x25, 0xa5, 0x6a, 0xb7, 0x56, 0x35, 0xa1, 0x42, 0x4a, 0xaa, 0xdf, 0xad, 0x2a, 0xad, 0x14, 0x94, 0x90, 0x54, 0x7a, 0x55, 0x55, 0x55, 0xa0, 0x22, 0x25, 0xd1, 0xaf, 0xa5, 0x2a, 0x55, 0x05, 0x48, 0x4a, 0x2a, 0xbd, 0xaa, 0x2a, 0x2a, 0xa8, 0x92, 0x90, 0xca, 0xaa, 0x54, 0x55, 0xad, 0x02, 0x44, 0x4a, 0xb2, 0x5e, 0x55, 0x2a, 0x2a, 0xa8, 0x90, 0x24, 0xa5, 0xb5, 0xaa, 0x52, 0x95, 0x04, 0x0a, 0x91, 0x28, 0xad, 0x4a, 0x55, 0x16, 0x42, 0x20, 0x42, 0xaa, 0xaa, 0x52, 0x6a, 0x55, 0x08, 0x82, 0x94, 0xaa, 0x7a, 0x55, 0x35, 0x0a, 0x91, 0x10, 0x20, 0x49, 0xa5, 0xaa, 0x6a, 0x55, 0x04, 0x40, 0x55, 0xaa, 0x7a, 0x91, 0x3a, 0x0d, 0x51, 0x02, 0x80, 0x4a, 0x65, 0x55, 0x55, 0x55, 0x04, 0x48, 0xaa, 0x54, 0xda, 0xaa, 0x3a, 0x0a, 0x91, 0x00, 0x11, 0xa9, 0x6a, 0xa9, 0x16, 0x55, 0x44, 0x52, 0xa4, 0x52, 0xd5, 0x52, 0x35, 0x8a, 0x10, 0x08, 0x29, 0x95, 0xaa, 0xaa, 0x4a, 0x55, 0x4a, 0xa1, 0x52, 0x6a, 0xad, 0x55, 0x1d, 0x95, 0x24, 0x0a, 0x85, 0xaa, 0x56, 0x55, 0x23, 0x15, 0x91, 0x54, 0x69, 0x79, 0x4b, 0x55, 0x2d, 0x6b, 0x4a, 0x51, 0x4a, 0xca, 0x92, 0xaa, 0x52, 0x96, 0x24, 0x95, 0xaa, 0xb5, 0xa4, 0x92, 0x15, 0x55, 0x49, 0xaa, 0xb4, 0x2a, 0x09, 0x25, 0x25, 0xad, 0x92, 0x12, 0xd5, 0x55, 0x52, 0x92, 0x54, 0x5b, 0xa4, 0xa4, 0xba, 0xa2, 0x84, 0x4a, 0x55, 0xaa, 0x55, 0x49, 0x6a, 0x95, 0x52, 0x11, 0x2a, 0xbb, 0x12, 0x92, 0xaa, 0x24, 0x25, 0xa6, 0x24, 0x56, 0xd5, 0xa4, 0xf2, 0x49, 0x45, 0x29, 0x29, 0xed, 0x2a, 0xa9, 0xb6, 0x52, 0x5b, 0x4b, 0x2a, 0x5b, 0x55, 0x45, 0x29, 0xd4, 0xaa, 0x54, 0x55, 0xb6, 0xab, 0x2a, 0xaa, 0x55, 0x55, 0x95, 0x2a, 0xd5, 0xde, 0x4a, 0x21, 0x48, 0xaa, 0x52, 0x25, 0xad, 0xaa, 0x55, 0x4a, 0x95, 0xad, 0xaa, 0x6a, 0xdb, 0xb6, 0x96, 0x24, 0x62, 0x6b, 0x95, 0x54, 0xaa, 0x6d, 0x55, 0x49, 0x59, 0xda, 0xaa, 0x2a, 0x6d, 0xb5, 0x57, 0x12, 0x52, 0xa7, 0x56, 0x55, 0xd5, 0x6a, 0xad, 0xa2, 0x94, 0x68, 0x55, 0x35, 0xad, 0x57, 0xb5, 0x14, 0x55, 0xaa, 0x55, 0x6b, 0xb5, 0xac, 0xaa, 0xa2, 0x24, 0x75, 0xd5, 0x36, 0x6d, 0xab, 0x54, 0x15, 0xa9, 0xac, 0xad, 0x6a, 0xaa, 0xaa, 0x4a, 0x4a, 0x55, 0xdb, 0x5a, 0x5d, 0x55, 0x15, 0x14, 0x54, 0x55, 0xb5, 0xd7, 0x36, 0xaa, 0x4a, 0x85, 0xa2, 0xaa, 0x7f, 0x55, 0x6b, 0x55, 0x91, 0x02, 0x54, 0x7d, 0xd5, 0x6a, 0x37, 0xaa, 0x4a, 0x4a, 0x48, 0xa5, 0x7f, 0x5b, 0x6d, 0x55, 0xa4, 0x02, 0x92, 0xea, 0xea, 0x56, 0x2b, 0x92, 0x12, 0x01, 0x40, 0xa8, 0xbd, 0x55, 0x55, 0x2a, 0xa4, 0x0a, 0x12, 0x45, 0xd7, 0xb5, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ================================================ FILE: src/BITMAPS/automatic.xbm ================================================ #define automatic_width 32 #define automatic_height 32 static const unsigned char automatic_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xaa, 0xaa, 0x00, 0x40, 0x55, 0x55, 0x01, 0x80, 0xff, 0xff, 0x00, 0x40, 0x01, 0x40, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x40, 0xe1, 0x47, 0x01, 0x80, 0x11, 0xc8, 0x00, 0x40, 0x09, 0x50, 0x01, 0x80, 0x75, 0xd7, 0x00, 0x40, 0xf5, 0x5f, 0x01, 0x80, 0x95, 0xd9, 0x00, 0x40, 0x65, 0x56, 0x01, 0x80, 0x35, 0xd0, 0x00, 0x40, 0xf5, 0x50, 0x01, 0x80, 0xc5, 0xd4, 0x00, 0x40, 0x05, 0x56, 0x01, 0x80, 0x05, 0xd6, 0x00, 0x40, 0xed, 0x53, 0x01, 0x80, 0xd9, 0xd8, 0x00, 0x40, 0x31, 0x4c, 0x01, 0x80, 0x21, 0xc2, 0x00, 0x40, 0xff, 0x7f, 0x01, 0x80, 0xaa, 0xaa, 0x00, 0x40, 0x55, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ================================================ FILE: src/BITMAPS/automatic.xpm ================================================ /* XPM */ static char *automatic[]={ "32 32 8 1", ". c None", "d c #000000", "b c #585858", "a c #808000", "# c #a0a0a0", "c c #c0c000", "e c #ffc0c0", "f c #ffffff", "................................", "................................", "................................", "......###################.......", "......#aaaaaaaaaaaaaaaaab.......", "......#abbbbbbbbbbbbbb#ab.......", "......#abccccccccccccc#ab.......", "......#abccccccccccccc#ab.......", "......#abccccddddddccc#ab.......", "......#abcccdeeeeeedcc#ab.......", "......#abccdeeeeeeeedc#ab.......", "......#abcdedddedddedc#ab.......", "......#abcdedddddddddc#ab.......", "......#abcdedffddffddc#ab.......", "......#abcdeeddeeddedc#ab.......", "......#abcdeddeeeeeedc#ab.......", "......#abcdeddddeeeedc#ab.......", "......#abcdeeeddeededc#ab.......", "......#abcdeeeeeeddedc#ab.......", "......#abcdeeeeeeddedc#ab.......", "......#abcddedddddeedc#ab.......", "......#abccddeddeeeddc#ab.......", "......#abcccddeeeeddcc#ab.......", "......#abccccdeeedcccc#ab.......", "......#a###############ab.......", "......#aaaaaaaaaaaaaaaaab.......", "......#bbbbbbbbbbbbbbbbbb.......", "................................", "................................", "................................", "................................", "................................"}; ================================================ FILE: src/BITMAPS/automatic1.xbm ================================================ #define automatic1_width 32 #define automatic1_height 32 static const unsigned char automatic1_bits[] = { 0x50, 0x00, 0x00, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x9e, 0x01, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x64, 0x03, 0x00, 0x00, 0x67, 0x06, 0x00, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x08, 0x53, 0x55, 0x55, 0x90, 0xa3, 0xaa, 0xaa, 0x10, 0xcf, 0xff, 0x7f, 0x20, 0xaa, 0x00, 0xa0, 0x20, 0xd6, 0x00, 0x60, 0x40, 0xa4, 0x00, 0xa0, 0x80, 0xc8, 0xe0, 0x67, 0x80, 0xb0, 0x10, 0xa8, 0x00, 0xd1, 0x08, 0x70, 0x00, 0xa1, 0x74, 0xa7, 0x00, 0xa2, 0xb4, 0x6b, 0x00, 0xc4, 0xf4, 0xab, 0x00, 0x84, 0x64, 0x66, 0x00, 0x88, 0x35, 0xa0, 0x00, 0x08, 0xf5, 0x60, 0x00, 0xf0, 0xc7, 0xb0, 0x00, 0xc0, 0x64, 0x70, 0x00, 0xa0, 0xe4, 0xb1, 0x00, 0xc0, 0xcc, 0x73, 0x00, 0xa0, 0x18, 0xbb, 0x00, 0xc0, 0x10, 0x6c, 0x00, 0xa0, 0xff, 0xbf, 0x00, 0x40, 0x55, 0x55, 0x00, 0xa0, 0xaa, 0xaa}; ================================================ FILE: src/BITMAPS/automatic1.xpm ================================================ /* XPM */ static char *automatic1[]={ "32 32 8 1", ". c None", "# c #000000", "d c #585858", "c c #808000", "b c #a0a0a0", "e c #c0c000", "f c #ffc0c0", "a c #ffffff", "....#a#.........................", "#...#a##........................", "##..#aa#........................", "a####aa##.......................", "aa##aaaa#.......................", "aa#aa##a##......................", "###aa##aa##.....................", "..#aaaaaa###....................", "...#aaaa##aa#bbbbbbbbbbbbbbbbbbb", "....#aa###aa.bcccccccccccccccccd", "....#aaa####abcddddddddddddddbcd", ".....#aaa#.#.bcdeeeeeeeeeeeeebcd", ".....#aaa##.#bcdeeeeeeeeeeeeebcd", "......#aaa#..bcdeeeeeeeeeeeeebcd", ".......#aaa#.bcdeeeee######eebcd", ".......#aaaa#bcdeeee#ffffff#ebcd", "........#aaa#bcdeee#ffffffff#bcd", "........#aaaa#cdee#f###f###ffbcd", ".........#aaa#cdee#f##a###a#fbcd", "..........#aaa#dee#f######a#fbcd", "..........#aaaa#ee#ff##ff##ffbcd", "...........#aaa##e#f##fffffffbcd", "...........#aaaa#e#f####fffffbcd", "............#####e#fff##ffff#bcd", ".............bcdee#ff##fffff#bcd", ".............bcdee#ff####fff#bcd", ".............bcdee##ff####ff#bcd", ".............bcdeee##fff##f##bcd", ".............bcdeeee#fffff##ebcd", ".............bcbbbbbbbbbbbbbbbcd", ".............bcccccccccccccccccd", ".............bdddddddddddddddddd"}; ================================================ FILE: src/BITMAPS/automatic2.xbm ================================================ #define automatic2_width 32 #define automatic2_height 32 static const unsigned char automatic2_bits[] = { 0xe0, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x9e, 0x01, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x64, 0x0f, 0x00, 0x00, 0x64, 0x12, 0x00, 0x00, 0x07, 0x72, 0x00, 0x00, 0x0c, 0x43, 0x55, 0x55, 0x88, 0xa6, 0xaa, 0xaa, 0x10, 0xc9, 0xff, 0x7f, 0x10, 0xb2, 0x00, 0xa0, 0x20, 0xc2, 0x00, 0x60, 0x20, 0xa4, 0x00, 0xa0, 0x40, 0xc4, 0xe0, 0x67, 0x40, 0xa8, 0x10, 0xa8, 0x80, 0xc8, 0x08, 0x70, 0x80, 0xb0, 0x74, 0xa7, 0x00, 0xd1, 0xb4, 0x6b, 0x00, 0xa1, 0xf4, 0xab, 0x00, 0xe2, 0x64, 0x66, 0x00, 0xc2, 0x34, 0xa0, 0x00, 0xc4, 0xf4, 0x60, 0x00, 0xc4, 0xc4, 0xb0, 0x00, 0xf8, 0x64, 0x70, 0x00, 0xa0, 0xe4, 0xb1, 0x00, 0xc0, 0xcc, 0x73, 0x00, 0xa0, 0x18, 0xbb, 0x00, 0xc0, 0x10, 0x6c, 0x00, 0xa0, 0xff, 0xbf, 0x00, 0x40, 0x55, 0x55, 0x00, 0xa0, 0xaa, 0xaa}; ================================================ FILE: src/BITMAPS/automatic2.xpm ================================================ /* XPM */ static char *automatic2[]={ "32 32 8 1", ". c None", "# c #000000", "d c #585858", "c c #808000", "b c #a0a0a0", "e c #c0c000", "f c #ffc0c0", "a c #ffffff", ".....#a#........................", "....#aa#........................", "#...#aa#........................", "a####aa##.......................", "aa##aaaa##......................", "aa#aa##a####....................", "aa#aa##aa#aa#...................", "###aaaaaa#aa###.................", "..##aaaa##aaabbbbbbbbbbbbbbbbbbb", "...#aaa#.##aabcccccccccccccccccd", "....#aaa#..#abcddddddddddddddbcd", "....#aaaa#..#bcdeeeeeeeeeeeeebcd", ".....#aaa#...bcdeeeeeeeeeeeeebcd", ".....#aaaa#..bcdeeeeeeeeeeeeebcd", "......#aaa#..bcdeeeee######eebcd", "......#aaaa#.bcdeeee#ffffff#ebcd", ".......#aaa#.bcdeee#ffffffff#bcd", ".......#aaaa#bcdee#f###f###ffbcd", "........#aaa#bcdee#f##a###a#fbcd", "........#aaaa#cdee#f######a#fbcd", ".........#aaa#cdee#ff##ff##ffbcd", ".........#aaaa#dee#f##fffffffbcd", "..........#aaa#dee#f####fffffbcd", "..........#aaa#dee#fff##ffff#bcd", "...........####dee#ff##fffff#bcd", ".............bcdee#ff####fff#bcd", ".............bcdee##ff####ff#bcd", ".............bcdeee##fff##f##bcd", ".............bcdeeee#fffff##ebcd", ".............bcbbbbbbbbbbbbbbbcd", ".............bcccccccccccccccccd", ".............bdddddddddddddddddd"}; ================================================ FILE: src/BITMAPS/automatic3.xbm ================================================ #define automatic3_width 32 #define automatic3_height 32 static const unsigned char automatic3_bits[] = { 0x33, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0d, 0x01, 0x00, 0x00, 0x66, 0x03, 0x00, 0x00, 0x64, 0x06, 0x00, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x08, 0x53, 0x55, 0x55, 0x90, 0xa3, 0xaa, 0xaa, 0x30, 0xc2, 0xff, 0x7f, 0x20, 0xa4, 0x00, 0xa0, 0x40, 0xc8, 0x00, 0x60, 0x80, 0xa8, 0x00, 0xa0, 0x80, 0xd0, 0xe0, 0x67, 0x00, 0xa1, 0x10, 0xa8, 0x00, 0xc2, 0x08, 0x70, 0x00, 0xc4, 0x74, 0xa7, 0x00, 0x84, 0xb5, 0x6b, 0x00, 0x08, 0xf5, 0xab, 0x00, 0x10, 0x66, 0x66, 0x00, 0x30, 0x34, 0xa0, 0x00, 0x20, 0xfc, 0x60, 0x00, 0xe0, 0xcf, 0xb0, 0x00, 0xc0, 0x64, 0x70, 0x00, 0xa0, 0xe4, 0xb1, 0x00, 0xc0, 0xcc, 0x73, 0x00, 0xa0, 0x18, 0xbb, 0x00, 0xc0, 0x10, 0x6c, 0x00, 0xa0, 0xff, 0xbf, 0x00, 0x40, 0x55, 0x55, 0x00, 0xa0, 0xaa, 0xaa}; ================================================ FILE: src/BITMAPS/automatic3.xpm ================================================ /* XPM */ static char *automatic3[]={ "32 32 8 1", "a c None", ". c #000000", "d c #585858", "c c #808000", "b c #a0a0a0", "e c #c0c000", "f c #ffc0c0", "# c #ffffff", "..##..aaaaaaaaaaaaaaaaaaaaaaaaaa", "#..###..aaaaaaaaaaaaaaaaaaaaaaaa", "##...##.aaaaaaaaaaaaaaaaaaaaaaaa", "##...##..aaaaaaaaaaaaaaaaaaaaaaa", ".#..####.aaaaaaaaaaaaaaaaaaaaaaa", "a..##..#..aaaaaaaaaaaaaaaaaaaaaa", "aa.##..##..aaaaaaaaaaaaaaaaaaaaa", "aa.######...aaaaaaaaaaaaaaaaaaaa", "aaa.####..##.bbbbbbbbbbbbbbbbbbb", "aaaa.##...###bcccccccccccccccccd", "aaaa..###.###bcddddddddddddddbcd", "aaaaa.####.##bcdeeeeeeeeeeeeebcd", "aaaaaa.####.#bcdeeeeeeeeeeeeebcd", "aaaaaaa.###.#bcdeeeeeeeeeeeeebcd", "aaaaaaa.####.bcdeeeee......eebcd", "aaaaaaaa.####.cdeeee.ffffff.ebcd", "aaaaaaaaa.####.deee.ffffffff.bcd", "aaaaaaaaaa.###.dee.f...f...ffbcd", "aaaaaaaaaa.####..e.f..#...#.fbcd", "aaaaaaaaaaa.####.e.f......#.fbcd", "aaaaaaaaaaaa.####..ff..ff..ffbcd", "aaaaaaaaaaaaa.####.f..fffffffbcd", "aaaaaaaaaaaaab.###......fffffbcd", "aaaaaaaaaaaaabc.....ff..ffff.bcd", "aaaaaaaaaaaaabcdee.ff..fffff.bcd", "aaaaaaaaaaaaabcdee.ff....fff.bcd", "aaaaaaaaaaaaabcdee..ff....ff.bcd", "aaaaaaaaaaaaabcdeee..fff..f..bcd", "aaaaaaaaaaaaabcdeeee.fffff..ebcd", "aaaaaaaaaaaaabcbbbbbbbbbbbbbbbcd", "aaaaaaaaaaaaabcccccccccccccccccd", "aaaaaaaaaaaaabdddddddddddddddddd"}; ================================================ FILE: src/BITMAPS/book_closed.xpm ================================================ /* XPM */ static char * book_closed_xpm[] = { "16 16 6 1", " c None s None", ". c black", "X c red", "o c yellow", "O c #808080", "# c white", " ", " .. ", " ..XX. ", " ..XXXXX. ", " ..XXXXXXXX. ", ".ooXXXXXXXXX. ", "..ooXXXXXXXXX. ", ".X.ooXXXXXXXXX. ", ".XX.ooXXXXXX.. ", " .XX.ooXXX..#O ", " .XX.oo..##OO. ", " .XX..##OO.. ", " .X.#OO.. ", " ..O.. ", " .. ", " "}; ================================================ FILE: src/BITMAPS/book_open.xpm ================================================ /* XPM */ static char * book_open_xpm[] = { "16 16 4 1", " c None s None", ". c black", "X c #808080", "o c white", " ", " .. ", " .Xo. ... ", " .Xoo. ..oo. ", " .Xooo.Xooo... ", " .Xooo.oooo.X. ", " .Xooo.Xooo.X. ", " .Xooo.oooo.X. ", " .Xooo.Xooo.X. ", " .Xooo.oooo.X. ", " .Xoo.Xoo..X. ", " .Xo.o..ooX. ", " .X..XXXXX. ", " ..X....... ", " .. ", " "}; ================================================ FILE: src/BITMAPS/change.xbm ================================================ #define change_width 32 #define change_height 32 static unsigned char change_bits[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xfd, 0xff, 0xb2, 0x01, 0x0b, 0xd8, 0xb3, 0x01, 0x0d, 0xd8, 0xfe, 0xff, 0xfb, 0xff, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xfd, 0xff, 0x02, 0x1b, 0x8b, 0x0d, 0x03, 0x1b, 0x8d, 0x0d, 0xfe, 0xff, 0xfb, 0xff, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xfd, 0xff, 0xda, 0x00, 0x6b, 0x03, 0xdb, 0x00, 0x6d, 0x03, 0xfe, 0xff, 0xfb, 0xff, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xfd, 0xff, 0x02, 0x1b, 0x8b, 0x0d, 0x03, 0x1b, 0x8d, 0x0d, 0xfe, 0xff, 0xfb, 0xff, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55}; ================================================ FILE: src/BITMAPS/col.xpm ================================================ /* XPM */ static char *col[]={ "16 16 8 1", ". c None", "# c #000000", "f c #303030", "e c #585858", "b c #808080", "d c #a0a0a0", "c c #c3c3c3", "a c #ffffff", "....#aaaaaaaaaa#", "....#aaaaaaaaaa#", "....############", "......#bca#bc#..", "......#bca#bc#..", "......#bca#bc#..", "......#bca#bc#..", "d.....#bca#bc#..", "ddd...#bca#bc#..", "eedd..#bca#bc#..", "eeeddd#bca#bc#..", "eeeeee#bca#bc#..", "ffeeee#bca#bc#..", "#ffe############", "##ff#aaaaaaaaaa#", "###f#aaaaaaaaaa#"}; ================================================ FILE: src/BITMAPS/convol.xbm ================================================ #define convol_width 32 #define convol_height 32 static unsigned char convol_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x84, 0x3c, 0xf3, 0x1e, 0xc6, 0xa0, 0x44, 0x02, 0xa4, 0x10, 0x62, 0x0e, 0xe4, 0x09, 0x81, 0x10, 0x84, 0x88, 0x80, 0x10, 0x8e, 0x88, 0x77, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x31, 0x86, 0x08, 0x42, 0x4a, 0xc9, 0x0c, 0x8e, 0x49, 0xa9, 0x08, 0x52, 0x4a, 0xee, 0x09, 0x52, 0x4a, 0x88, 0x08, 0x8c, 0x31, 0x86, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x91, 0x67, 0x0c, 0x92, 0x99, 0x90, 0x12, 0x52, 0x91, 0x43, 0x08, 0xd2, 0x13, 0x24, 0x04, 0x12, 0x11, 0x14, 0x02, 0x0c, 0xb9, 0xf3, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x21, 0xe6, 0x19, 0x48, 0x32, 0x09, 0x25, 0x0c, 0x29, 0x86, 0x10, 0x90, 0x78, 0x49, 0x08, 0x50, 0x20, 0x49, 0x04, 0xce, 0x23, 0x46, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ================================================ FILE: src/BITMAPS/dropper.xpm ================================================ /* XPM */ static char * dropper_xpm[] = { /* width height ncolors cpp [x_hot y_hot] */ "16 16 4 1 -1 -1", /* colors */ " s none m none c none", ". s iconColor1 m black c black", "X s iconColor2 m white c white", "o s iconGray2 m gray c #909090909090", /* pixels */ " ... ", " .XX... ", " .X.... ", " .......", " .......", " .........", " XX......o", " X.XX.oooo ", " X.XXX.o ", " X.XXXooo ", " X.XXXo ", " X.XXXo ", " X.XXXo ", " XXXXo ", "XXXXo ", "XXoo "}; ================================================ FILE: src/BITMAPS/dropper_msk.xbm ================================================ #define dropper_msk_width 16 #define dropper_msk_height 16 static unsigned char dropper_msk_bits[] = { 0x00, 0x3c, 0x00, 0x7e, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x80, 0xff, 0x80, 0x7f, 0xc0, 0x07, 0xe0, 0x07, 0xf0, 0x01, 0xf8, 0x00, 0x7c, 0x00, 0x3e, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x03, 0x00}; ================================================ FILE: src/BITMAPS/dropper_src.xbm ================================================ #define dropper_src_width 16 #define dropper_src_height 16 static unsigned char dropper_src_bits[] = { 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x40, 0x03, 0xa0, 0x03, 0xd0, 0x01, 0xe8, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x03, 0x00}; ================================================ FILE: src/BITMAPS/floppy.xpm ================================================ /* XPM */ static char *floppy[]={ "16 16 10 1", ". c None", "# c #000000", "e c #000080", "c c #0000c0", "a c #0000ff", "b c #303030", "g c #808080", "h c #a0a0a4", "f c #c0c0c0", "d c #ffffff", "................", ".......#........", "......#ab#......", ".....#acdd##....", "....#acddddd##..", "...#acdddddddc##", "..#aeaacddddcae#", ".#aefeeaacdcae#.", "#aefaffeeacae#..", "#egfffhaaeae#...", ".##g.haaeae#....", "...##gaeae#.....", ".....##ee#......", ".......##.......", "................", "................"}; ================================================ FILE: src/BITMAPS/image.xbm ================================================ #define program_width 32 #define program_height 32 static unsigned char program_bits[] = { 0x55, 0x55, 0x55, 0x55, 0xfe, 0xff, 0xff, 0xff, 0x03, 0x10, 0x00, 0x40, 0x02, 0x18, 0x00, 0xc0, 0x03, 0x08, 0x00, 0x40, 0x02, 0x00, 0x38, 0xc0, 0x03, 0x1c, 0xcc, 0x40, 0x02, 0x34, 0xc4, 0xc0, 0x03, 0xa0, 0x00, 0x40, 0x02, 0x90, 0x78, 0xc0, 0x03, 0x90, 0x5c, 0x40, 0x02, 0xc0, 0x18, 0xc0, 0x03, 0x20, 0x00, 0x40, 0x02, 0x30, 0x00, 0xc0, 0x03, 0x10, 0x00, 0x40, 0x02, 0x18, 0x06, 0xc0, 0x03, 0x08, 0x0c, 0x40, 0x02, 0xd0, 0x08, 0xc0, 0x03, 0xb0, 0x0f, 0x40, 0x02, 0x00, 0x00, 0xc0, 0x03, 0x80, 0x00, 0x40, 0x02, 0xc0, 0x01, 0xc0, 0x03, 0x80, 0x7f, 0x40, 0x02, 0x00, 0x39, 0xc0, 0x03, 0x00, 0x00, 0x40, 0x02, 0x00, 0x01, 0xe0, 0x03, 0x00, 0x1f, 0x60, 0x02, 0x00, 0x1c, 0xf0, 0x03, 0x00, 0x30, 0x58, 0x02, 0x00, 0xe0, 0xfe, 0xff, 0xff, 0xff, 0x7f, 0xaa, 0xaa, 0xaa, 0xaa}; ================================================ FILE: src/BITMAPS/kill.xbm ================================================ #define kill_width 16 #define kill_height 16 static unsigned char kill_bits[] = { 0x04, 0xc0, 0x0a, 0xe0, 0x16, 0x70, 0x2e, 0xb8, 0x5c, 0x5c, 0xb8, 0x2e, 0x70, 0x17, 0xe0, 0x0a, 0xc0, 0x05, 0xe0, 0x0a, 0x70, 0x17, 0xb8, 0x2e, 0x5c, 0x5c, 0x2e, 0xb8, 0x16, 0x70, 0x0a, 0xe0}; ================================================ FILE: src/BITMAPS/mag_msk.xbm ================================================ #define mag_msk_width 16 #define mag_msk_height 16 static unsigned char mag_msk_bits[] = { 0xf0, 0x00, 0xfc, 0x03, 0xfe, 0x07, 0xfe, 0x07, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xfe, 0x07, 0xfe, 0x0f, 0xfc, 0x1f, 0xf0, 0x3e, 0x00, 0x7c, 0x00, 0xf8, 0x00, 0xf0, 0x00, 0xe0}; ================================================ FILE: src/BITMAPS/magin.xpm ================================================ /* XPM */ static char * magin_xpm[] = { /* width height ncolors cpp [x_hot y_hot] */ "16 16 4 1 -1 -1", /* colors */ " s none m none c none", ". s iconColor1 m black c black", "X s iconColor2 m white c white", "o s iconGray1 m white c #dededededede", /* pixels */ " .... ", " ..XXXX.. ", " .XXXXXXXX. ", " .XXX..XXX. ", ".XXXX..XXXX. ", ".XX......XX.o ", ".XX......XX.o ", ".XXXX..XXXX.o ", " .XXX..XXX.o ", " .XXXXXXXX..o ", " ..XXXX...X. ", " ....o.X.X. ", " ooo o...X. ", " ...X.", " ...X", " ..."}; ================================================ FILE: src/BITMAPS/magin_src.xbm ================================================ #define magin_src_width 16 #define magin_src_height 16 static unsigned char magin_src_bits[] = { 0x00, 0x00, 0xf0, 0x00, 0xfc, 0x03, 0x9c, 0x03, 0x9e, 0x07, 0x06, 0x06, 0x06, 0x06, 0x9e, 0x07, 0x9c, 0x03, 0xfc, 0x03, 0xf0, 0x08, 0x00, 0x14, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00}; ================================================ FILE: src/BITMAPS/magout.xpm ================================================ /* XPM */ static char * magout_xpm[] = { /* width height ncolors cpp [x_hot y_hot] */ "16 16 4 1 -1 -1", /* colors */ " s none m none c none", ". s iconColor1 m black c black", "X s iconColor2 m white c white", "o s iconGray1 m white c #dededededede", /* pixels */ " .... ", " ..XXXX.. ", " .XXXXXXXX. ", " .XXXXXXXX. ", ".XXXXXXXXXX. ", ".XX......XX.o ", ".XX......XX.o ", ".XXXXXXXXXX.o ", " .XXXXXXXX.o ", " .XXXXXXXX..o ", " ..XXXX...X. ", " ....o.X.X. ", " ooo o...X. ", " ...X.", " ...X", " ..."}; ================================================ FILE: src/BITMAPS/magout_src.xbm ================================================ #define magout_src_width 16 #define magout_src_height 16 static unsigned char magout_src_bits[] = { 0x00, 0x00, 0xf0, 0x00, 0xfc, 0x03, 0xfc, 0x03, 0xfe, 0x07, 0x06, 0x06, 0x06, 0x06, 0xfe, 0x07, 0xfc, 0x03, 0xfc, 0x03, 0xf0, 0x08, 0x00, 0x14, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00}; ================================================ FILE: src/BITMAPS/mini_page.xpm ================================================ /* XPM */ static char * mini_page_xpm[] = { "16 16 4 1", " c None s None", ". c black", "X c white", "o c #808080", " ", " ....... ", " .XXXXX.. ", " .XoooX.X. ", " .XXXXX.... ", " .XooooXoo.o ", " .XXXXXXXX.o ", " .XooooooX.o ", " .XXXXXXXX.o ", " .XooooooX.o ", " .XXXXXXXX.o ", " .XooooooX.o ", " .XXXXXXXX.o ", " ..........o ", " oooooooooo ", " "}; ================================================ FILE: src/BITMAPS/morph.xbm ================================================ #define morph_width 32 #define morph_height 32 static unsigned char morph_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xfe, 0x30, 0x00, 0x00, 0xba, 0x28, 0x00, 0x00, 0x12, 0x99, 0x00, 0x00, 0xd6, 0xde, 0x00, 0x00, 0xe0, 0x6d, 0x03, 0x00, 0xb8, 0x10, 0x00, 0x00, 0x7c, 0x07, 0x00, 0x00, 0xce, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0xc0, 0x00, 0xe0, 0x03, 0xe6, 0x00, 0xf0, 0x07, 0xfe, 0x00, 0xf8, 0x0f, 0xfc, 0x01, 0x60, 0x13, 0xf0, 0x03, 0x60, 0x33, 0xfc, 0x07, 0x60, 0x7e, 0xfe, 0x3f, 0xe0, 0xfe, 0xfe, 0x7f, 0xc0, 0xf1, 0xff, 0x7f, 0xc0, 0xff, 0xfe, 0x7f, 0x00, 0x7f, 0xfa, 0xff, 0x00, 0x30, 0xfc, 0xff, 0x00, 0x10, 0xfe, 0x7f, 0x00, 0x00, 0xff, 0x33, 0x00, 0x00, 0x87, 0x01}; ================================================ FILE: src/BITMAPS/paint.xpm ================================================ /* XPM */ static char * paint_xpm[] = { /* width height ncolors cpp [x_hot y_hot] */ "16 16 4 1 -1 -1", /* colors */ " s none m none c none", ". s iconColor2 m white c white", "x s iconColor1 m black c black", "a s iconGray2 m gray c #909090909090", /* pixels */ " x..xa", " x..xaa", " x..xa ", " x..xaa ", " x..xa ", " x..xaa ", " x..xa ", " xxxxaa ", " x..xxa ", " x...xxa ", " x..axxa ", " x..axxaa ", " x.axxaa ", " x.axxaa ", " xxxxaaa ", " aaaa "}; ================================================ FILE: src/BITMAPS/pan.xpm ================================================ /* XPM */ static char * pan_xpm[] = { /* width height ncolors cpp [x_hot y_hot] */ "16 16 3 1 -1 -1", /* colors */ " s none m none c none", ". s iconColor1 m black c black", "X c #929292929292", /* pixels */ " . ", " ... ", " ..... ", " .XXX ", " .X ", " . .X . ", " .. .X .. ", "............... ", " ..XXXX.XXXX..XX", " .X .X .XX ", " X .X X ", " .X ", " ..... ", " ...X ", " .X ", " X "}; ================================================ FILE: src/BITMAPS/program.xbm ================================================ #define program_width 32 #define program_height 32 static unsigned char program_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x30, 0xf8, 0x38, 0x00, 0x28, 0x20, 0x44, 0x7c, 0x24, 0x10, 0x44, 0x00, 0x22, 0x10, 0x44, 0x7c, 0x7e, 0x10, 0x64, 0x01, 0x20, 0x10, 0x98, 0x00, 0x20, 0x12, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x03, 0x40, 0x00, 0x46, 0x04, 0x30, 0x00, 0x04, 0x04, 0x48, 0x7c, 0x04, 0x02, 0x48, 0x00, 0x04, 0x01, 0x30, 0x7c, 0x84, 0x00, 0x08, 0x00, 0x44, 0x00, 0x70, 0x00, 0xce, 0x07, 0x88, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ================================================ FILE: src/BITMAPS/select.xpm ================================================ /* XPM */ static char * select_xpm[] = { /* width height ncolors cpp [x_hot y_hot] */ "16 16 5 1 -1 -1", /* colors */ " s iconColor2 m white c white", ". s iconColor1 m black c black", "X s iconGray2 m white c #bdbdbdbdbdbd", "o s none m none c none", "O s iconGray3 m white c #adadadadadad", /* pixels */ " .", " XXXXXXXXXXXXXX.", " X............X.", " X. .ooo X.", " X. OOOOO.ooo X.", " X. OX.XO.ooo X.", " X. O...O.ooo X.", " X. O.X.O.ooo X.", " X. OOOOO.ooo X.", " X........ooo X.", " X.oooooooooo X.", " X.oooooooooo X.", " X.oooooooooo X.", " X. X.", " XXXXXXXXXXXXXX.", "................"}; ================================================ FILE: src/BITMAPS/separator.xpm ================================================ /* XPM */ static char *separator[]={ "64 4 6 1", "# c #000040", ". c #0000ff", "c c #0080ff", "a c #58a8ff", "b c #a8dcff", "d c #ffffff", ".##############################################################.", "a..............................................................a", "bbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb", "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"}; ================================================ FILE: src/BITMAPS/slider.xbm ================================================ #define slider_width 32 #define slider_height 32 static unsigned char slider_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x3f, 0x04, 0x36, 0x00, 0x20, 0x04, 0x36, 0x00, 0x20, 0xfc, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ================================================ FILE: src/BITMAPS/toolbox_closed.xpm ================================================ /* XPM */ static char *toolbox_closed[]={ "16 16 10 1", ". c None", "a c #000000", "b c #2b1702", "c c #482704", "f c #8c4c07", "h c #ca6d0a", "# c #cacbcb", "d c #ed800c", "g c #ffff00", "e c #ffffff", ".............#..", ".........abcc...", ".......ccabdec..", ".....cccfabeecc.", "...ccccffaddddc.", "..cfffccddddcfc.", ".bfffffcddccgfc.", ".afffffcacgggdc.", ".bbbcccccfgaddf.", ".cfhhhhbffgdddf.", ".cffhhhcddddddf.", ".cfffhhcddddff..", ".ccffhhcddff....", ".accffhcff......", ".aaccccc........", "................"}; ================================================ FILE: src/BITMAPS/toolbox_open.xpm ================================================ /* XPM */ static char *toolbox_open[]={ "16 16 12 1", ". c None", "b c #000000", "h c #2b1702", "a c #482704", "c c #82683b", "i c #8c4c07", "e c #8e8e8e", "j c #ca6d0a", "# c #cacbcb", "g c #ed800c", "d c #ffc0c0", "f c #ffff00", ".............#..", "...aab..........", ".baacca.........", "aaddccca........", "dddddccca.......", "dddddddaaaaaaaaa", "aadddaaeeecaafga", "gabaaeeeebafffga", "cchhhaaaaaafbggi", "haaijjjjhggfgggi", "..aiijjjaggggggi", "..aiiijjaggggii.", "..aaiijjaggii...", "..baaiijaii.....", "..bbaaaaa.......", "................"}; ================================================ FILE: src/BITMAPS/tools.xpm ================================================ /* XPM */ static char *tools[]={ "16 16 7 1", ". c None", "# c #000000", "e c #00c0c0", "c c #808080", "a c #a0a0a0", "b c #c0ffff", "d c #c3c3c3", "................", "..........####..", ".........#abc...", ".........#b#..#.", ".........#bd#c#.", "........#bdeee#.", ".......#bde###..", "......#bde#.....", ".....#bde#......", "....#bde#.......", "...#bde#........", "..#bde#.........", ".#bde#..........", "#bee#...........", "#ee#............", ".##............."}; ================================================ FILE: src/BITMAPS/watch_1.xbm ================================================ #define watch_1_width 16 #define watch_1_height 16 static unsigned char watch_1_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0xf9, 0xcf, 0xf9, 0xcf, 0xfc, 0x9f, 0xfc, 0x9f, 0x04, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x79, 0xcf, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_2.xbm ================================================ #define watch_2_width 16 #define watch_2_height 16 static unsigned char watch_2_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0xf9, 0xcf, 0xe9, 0xcf, 0xdc, 0x9f, 0xbc, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x79, 0xcf, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_3.xbm ================================================ #define watch_3_width 16 #define watch_3_height 16 static unsigned char watch_3_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0x79, 0xcf, 0x79, 0xcf, 0x7c, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x79, 0xcf, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_4.xbm ================================================ #define watch_4_width 16 #define watch_4_height 16 static unsigned char watch_4_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0xf9, 0xcf, 0xf9, 0xcb, 0xfc, 0x9d, 0xfc, 0x9e, 0x7c, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x79, 0xcf, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_5.xbm ================================================ #define watch_5_width 16 #define watch_5_height 16 static unsigned char watch_5_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0xf9, 0xcf, 0xf9, 0xcf, 0xfc, 0x9f, 0xfc, 0x9f, 0x7c, 0x90, 0x7c, 0x9f, 0x7c, 0x9f, 0x79, 0xcf, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_6.xbm ================================================ #define watch_6_width 16 #define watch_6_height 16 static unsigned char watch_6_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0xf9, 0xcf, 0xf9, 0xcf, 0xfc, 0x9f, 0xfc, 0x9f, 0x7c, 0x9f, 0x7c, 0x9e, 0x7c, 0x9d, 0x79, 0xcb, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_7.xbm ================================================ #define watch_7_width 16 #define watch_7_height 16 static unsigned char watch_7_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0xf9, 0xcf, 0xf9, 0xcf, 0xfc, 0x9f, 0xfc, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x7c, 0x9f, 0x79, 0xcf, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_8.xbm ================================================ #define watch_8_width 16 #define watch_8_height 16 static unsigned char watch_8_bits[] = { 0x1f, 0x9c, 0x07, 0x10, 0xe3, 0x23, 0xf9, 0xcf, 0xf9, 0xcf, 0xfc, 0x9f, 0xfc, 0x9f, 0x7c, 0x9f, 0x3c, 0x9f, 0x5c, 0x9f, 0x69, 0xcf, 0x79, 0xcf, 0xe3, 0xe3, 0x07, 0xf0, 0x1f, 0xfc, 0xff, 0xff}; ================================================ FILE: src/BITMAPS/watch_msk.xbm ================================================ #define watch_msk_width 16 #define watch_msk_height 16 #define watch_msk_x_hot 7 #define watch_msk_y_hot 7 static unsigned char watch_msk_bits[] = { 0xe0, 0x63, 0xf8, 0xef, 0xfc, 0xdf, 0xfe, 0xbf, 0xfe, 0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x7f, 0xf8, 0x3f, 0xf0, 0x1f, 0xc0, 0x07}; ================================================ FILE: src/Makefile.am ================================================ SUBDIRS = BITMAPS # need this to keep autoconf quiet, but we don't actually use it ... see the # bison rule below YACC = bison # windows resource files ... see .rc.o rule later SUFFIXES = .rc # only build the cli wrapper on win32 if OS_WIN32 bin_PROGRAMS = nip2 nip2-cli nip2_cli_SOURCES = nip2-cli.c nip2_CFLAGS="-mwindows" CLI_DIST = else bin_PROGRAMS = nip2 CLI_DIST = nip2-cli.c endif nip2_SOURCES = \ vipsobject.c \ vipsobject.h \ plotmodel.c \ plotmodel.h \ progress.c \ progress.h \ panechild.c \ panechild.h \ plotpresent.c \ plotpresent.h \ plotstatus.c \ plotstatus.h \ floatwindow.c \ floatwindow.h \ vobject.c \ vobject.h \ action.c \ action.h \ boxes.c \ boxes.h \ popupbutton.c \ popupbutton.h \ imageheader.c \ imageheader.h \ preview.c \ preview.h \ builtin.c \ builtin.h \ icontainer.c \ icontainer.h \ iobject.c \ iobject.h \ class.c \ class.h \ classmodel.c \ classmodel.h \ colour.c \ colour.h \ colourdisplay.c \ colourdisplay.h \ colourview.c \ colourview.h \ column.c \ column.h \ columnview.c \ columnview.h \ compile.c \ compile.h \ conversion.c \ conversion.h \ conversionview.c \ conversionview.h \ doubleclick.c \ doubleclick.h \ dump.c \ dump.h \ expr.c \ expr.h \ filemodel.c \ filemodel.h \ pane.c \ pane.h \ pathname.c \ pathname.h \ fontname.c \ fontname.h \ group.c \ group.h \ pathnameview.c \ pathnameview.h \ fontnameview.c \ fontnameview.h \ filesel.c \ filesel.h \ graphwindow.c \ graphwindow.h \ graphicview.c \ graphicview.h \ gtkutil.c \ gtkutil.h \ heap.c \ heap.h \ heapmodel.c \ heapmodel.h \ helpindex.h \ nipmarshal.h \ nipmarshal.c \ iarrow.c \ iarrow.h \ value.c \ value.h \ valueview.c \ valueview.h \ idialog.c \ idialog.h \ iimage.c \ iimage.h \ iimageview.c \ iimageview.h \ imagedisplay.c \ imagedisplay.h \ log.c \ log.h \ error.c \ error.h \ managed.c \ managed.h \ managedfile.c \ managedfile.h \ managedgvalue.c \ managedgvalue.h \ managedgobject.c \ managedgobject.h \ managedstring.c \ managedstring.h \ imageinfo.c \ imageinfo.h \ imagemodel.c \ imagemodel.h \ imagepresent.c \ imagepresent.h \ imageview.c \ imageview.h \ ip.h \ iregion.c \ iregion.h \ iregiongroup.c \ iregiongroup.h \ iregiongroupview.c \ iregiongroupview.h \ iregionview.c \ iregionview.h \ itext.c \ itext.h \ itextview.c \ itextview.h \ iwindow.c \ iwindow.h \ parse.y \ parser.h \ prefs.c \ prefs.h \ prefworkspaceview.c \ prefworkspaceview.h \ prefcolumnview.c \ prefcolumnview.h \ lex.l \ link.c \ link.h \ main.c \ main.h \ mainw.c \ mainw.h \ matrix.c \ matrix.h \ matrixview.c \ matrixview.h \ plot.c \ plot.h \ plotview.c \ plotview.h \ plotwindow.c \ plotwindow.h \ model.c \ model.h \ option.c \ option.h \ optionview.c \ optionview.h \ formula.c \ formula.h \ paintboxview.c \ paintboxview.h \ path.c \ path.h \ predicate.c \ predicate.h \ program.c \ program.h \ string.c \ istring.h \ number.c \ number.h \ expression.c \ expression.h \ expressionview.c \ expressionview.h \ stringview.c \ stringview.h \ editview.c \ editview.h \ numberview.c \ numberview.h \ real.c \ real.h \ vector.c \ vector.h \ reduce.c \ reduce.h \ regionview.c \ regionview.h \ rhs.c \ rhs.h \ rhsview.c \ rhsview.h \ row.c \ row.h \ rowview.c \ rowview.h \ secret.c \ secret.h \ slider.c \ slider.h \ sliderview.c \ sliderview.h \ clock.c \ clock.h \ spin.c \ spin.h \ statusview.c \ statusview.h \ subcolumn.c \ subcolumn.h \ subcolumnview.c \ subcolumnview.h \ symbol.c \ symbol.h \ toggle.c \ toggle.h \ toggleview.c \ toggleview.h \ tool.c \ tool.h \ toolkit.c \ toolkit.h \ toolkitgroup.c \ toolkitgroup.h \ toolkitgroupview.c \ toolkitgroupview.h \ toolkitview.c \ toolkitview.h \ defbrowser.c \ defbrowser.h \ toolkitbrowser.c \ toolkitbrowser.h \ toolview.c \ toolview.h \ trace.c \ trace.h \ tree.c \ tree.h \ tslider.c \ tslider.h \ util.c \ util.h \ view.c \ view.h \ call.c \ call.h \ cache.c \ cache.h \ watch.c \ watch.h \ workspace.c \ workspace.h \ workspacegroup.c \ workspacegroup.h \ workspacegroupview.c \ workspacegroupview.h \ workspacedefs.c \ workspacedefs.h \ workspaceroot.c \ workspaceroot.h \ workspaceview.c \ workspaceview.h if OS_WIN32 nip2_SOURCES += \ nip2-icon.rc endif helpindex.h: ./makehelpindex.pl $(prefix) > helpindex.h nipmarshal.h: glib-genmarshal --prefix=nip --header nipmarshal.list > nipmarshal.h nipmarshal.c: echo "#include \"nipmarshal.h\"" > nipmarshal.c glib-genmarshal --prefix=nip --body nipmarshal.list >> nipmarshal.c .rc.o: cp ${top_srcdir}/share/nip2/data/nip2-icon.ico . ${WINDRES} $< -o $@ # we have to replace the standard .y.c rule: we are a bison-only GLR parser, # so we can't use autoconf's preferred -y yacc-compatibility stuff .y.c: $(BISON) --defines=$*.h -o $*.c $< nip2-model.o model.o: model.c parse.c AM_CPPFLAGS = @IP_CFLAGS@ LDADD = @IP_CFLAGS@ @IP_LIBS@ AM_LDFLAGS = @LDFLAGS@ dist-hook: ${RM} ${distdir}/parse.c ${distdir}/lex.c CLEANFILES = parse.c parse.h lex.c tags EXTRA_DIST = makehelpindex.pl helpindex.h \ nipmarshal.h nipmarshal.c nipmarshal.list \ $(CLI_DIST) ================================================ FILE: src/action.c ================================================ /* actions on the graph */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* Index with binop or uop. */ const char *operator_table[] = { "none", /* BI_NONE */ "add", /* BI_ADD */ "subtract", /* BI_SUB */ "remainder", /* BI_REM */ "power", /* BI_POW */ "subscript", /* BI_SELECT */ "left_shift", /* BI_LSHIFT */ "right_shift", /* BI_RSHIFT */ "divide", /* BI_DIV */ "join", /* BI_JOIN */ "dot", /* BI_DOT */ "comma", /* BI_COMMA */ "multiply", /* BI_MUL */ "logical_and", /* BI_LAND */ "logical_or", /* BI_LOR */ "bitwise_and", /* BI_BAND */ "bitwise_or", /* BI_BOR */ "eor", /* BI_EOR */ "equal", /* BI_EQ */ "not_equal", /* BI_NOTEQ */ "pointer_equal", /* BI_PEQ */ "pointer_not_equal", /* BI_PNOTEQ */ "less", /* BI_LESS */ "less_equal", /* BI_LESSEQ */ "none", /* BI_MORE */ "none", /* BI_MOREEQ */ "if_then_else", /* BI_IF */ "cons", /* BI_CONS */ "none", /* UN_NONE */ "cast_signed_char", /* UN_CSCHAR */ "cast_unsigned_char", /* UN_CUCHAR */ "cast_signed_short", /* UN_CSSHORT */ "cast_unsigned_short", /* UN_CUSHORT */ "cast_signed_int", /* UN_CSINT */ "cast_unsigned_int", /* UN_CUINT */ "cast_float", /* UN_CFLOAT */ "cast_double", /* UN_CDOUBLE */ "cast_complex", /* UN_CCOMPLEX */ "cast_double_complex", /* UN_CDCOMPLEX */ "unary_minus", /* UN_MINUS */ "negate", /* UN_NEG */ "complement", /* UN_COMPLEMENT */ "unary_plus" /* UN_PLUS */ }; const int noperator_table = IM_NUMBER( operator_table ); /* Bad bop error. */ static void action_boperror( Reduce *rc, Compile *compile, const char *str, int op, const char *name, PElement *a, PElement *b ) { const char *top_str = str ? str : _( "Bad arguments." ); const char *op_name = op >= 0 ? decode_BinOp( op ) : name; char txt[MAX_ERROR_FRAG]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char txt2[MAX_ERROR_FRAG]; VipsBuf buf2 = VIPS_BUF_STATIC( txt2 ); char txt3[MAX_ERROR_FRAG]; VipsBuf buf3 = VIPS_BUF_STATIC( txt3 ); itext_value_ev( rc, &buf, a ); itext_value_ev( rc, &buf2, b ); if( compile ) { /* Expands to eg. 'bad args to "+", called from "fred"' */ vips_buf_appends( &buf3, _( "Called from" ) ); vips_buf_appends( &buf3, " " ); compile_name( compile, &buf3 ); } error_top( "%s", top_str ); error_sub( _( "Error in binary %s.\n" "left = %s\n" "right = %s\n%s" ), op_name, vips_buf_all( &buf ), vips_buf_all( &buf2 ), vips_buf_all( &buf3 ) ); reduce_throw( rc ); } /* Member not found in class instance error. */ static void action_nomerror( Reduce *rc, Compile *compile, PElement *a, PElement *b ) { char txt[500]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char txt2[MAX_ERROR_FRAG]; VipsBuf buf2 = VIPS_BUF_STATIC( txt2 ); char txt3[MAX_ERROR_FRAG]; VipsBuf buf3 = VIPS_BUF_STATIC( txt3 ); if( PEISCLASS( a ) ) symbol_qualified_name( PEGETCLASSCOMPILE( a )->sym, &buf3 ); else if( PEISSYMREF( a ) ) symbol_qualified_name( PEGETSYMREF( a ), &buf3 ); else if( PEISSYMBOL( a ) ) symbol_qualified_name( PEGETSYMBOL( a ), &buf3 ); else if( PEISCOMPILEREF( a ) ) symbol_qualified_name( PEGETCOMPILE( a )->sym, &buf3 ); else vips_buf_appends( &buf3, "" ); itext_value_ev( rc, &buf2, b ); vips_buf_appendf( &buf, _( "Member \"%s\" not found in class \"%s\"." ), vips_buf_all( &buf2 ), vips_buf_all( &buf3 ) ); vips_buf_appendf( &buf, "\n" ); vips_buf_rewind( &buf3 ); itext_value_ev( rc, &buf3, a ); vips_buf_appendf( &buf, " " ); vips_buf_appendf( &buf, _( "object = %s" ), vips_buf_all( &buf3 ) ); vips_buf_appendf( &buf, "\n" ); vips_buf_appendf( &buf, " " ); vips_buf_appendf( &buf, _( "tag = %s" ), vips_buf_all( &buf2 ) ); vips_buf_appendf( &buf, "\n" ); vips_buf_rewind( &buf3 ); symbol_qualified_name( compile->sym, &buf3 ); vips_buf_appendf( &buf, _( "Reference attempted in \"%s\"." ), vips_buf_all( &buf3 ) ); vips_buf_appendf( &buf, "\n" ); error_top( _( "Member not found." ) ); error_sub( "%s", vips_buf_all( &buf ) ); reduce_throw( rc ); } /* Bad uop error. */ static void action_uoperror( Reduce *rc, Compile *compile, const char *str, int op, const char *name, PElement *a ) { const char *top_str = str ? str : _( "Bad argument." ); const char *op_name = op >= 0 ? decode_UnOp( op ) : name; char txt[MAX_ERROR_FRAG]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char txt2[MAX_ERROR_FRAG]; VipsBuf buf2 = VIPS_BUF_STATIC( txt2 ); itext_value_ev( rc, &buf, a ); if( compile ) { /* Expands to eg. 'bad args to "+", called from "fred"' */ vips_buf_appends( &buf2, _( "Called from" ) ); vips_buf_appends( &buf2, " " ); compile_name( compile, &buf2 ); } error_top( "%s", top_str ); error_sub( _( "Error in unary %s.\n" "argument = %s\n%s" ), op_name, vips_buf_all( &buf ), vips_buf_all( &buf2 ) ); reduce_throw( rc ); } /* Clip real part of number a to a range. */ static void action_set_range( Reduce *rc, double mn, double mx, PElement *a, PElement *out ) { Heap *heap = rc->heap; double d; /* Get real part. */ if( PEISREAL( a ) ) d = PEGETREAL( a ); else d = PEGETREALPART( a ); if( d < mn ) d = mn; else if( d > mx ) d = mx; else d = (int) d; if( !heap_real_new( heap, d, out ) ) reduce_throw( rc ); } /* EOR two things. */ static void action_proc_eor( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISBOOL( a ) && PEISBOOL( b ) ) { PEPUTP( out, ELEMENT_BOOL, PEGETBOOL( a ) ^ PEGETBOOL( b ) ); } else if( PEISREAL( a ) && PEISREAL( b ) ) { int v1 = PEGETREAL( a ); int v2 = PEGETREAL( b ); if( !heap_real_new( heap, v1 ^ v2, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_eorimage", PEGETII( a ), PEGETII( b ) ); else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_eorimageconst", PEGETII( a ), (int) PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_eorimageconst", PEGETII( b ), (int) PEGETREAL( a ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* OR two things. */ static void action_proc_bor( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { int v1 = PEGETREAL( a ); int v2 = PEGETREAL( b ); if( !heap_real_new( heap, v1 | v2, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_orimage", PEGETII( a ), PEGETII( b ) ); else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_orimageconst", PEGETII( a ), (int) PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_orimageconst", PEGETII( b ), (int) PEGETREAL( a ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* AND two things. */ static void action_proc_band( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_andimage", PEGETII( a ), PEGETII( b ) ); else if( PEISREAL( a ) && PEISREAL( b ) ) { int v1 = PEGETREAL( a ); int v2 = PEGETREAL( b ); if( !heap_real_new( heap, v1 & v2, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_andimageconst", PEGETII( a ), (int) PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_andimageconst", PEGETII( b ), (int) PEGETREAL( a ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } static void * action_proc_dot_add_link( Expr *expr, Symbol *child ) { return( link_add( child, expr, TRUE ) ); } static char * action_proc_dot_tag( Reduce *rc, PElement *b, char *tag, int n ) { if( PEISTAG( b ) ) return( PEGETTAG( b ) ); else if( reduce_is_string( rc, b ) ) { (void) reduce_get_string( rc, b, tag, n ); return( tag ); } return( NULL ); } /* Extract field from object. Be careful, a can be equal to out. */ static void action_proc_dot( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { char tag[256]; char *p; if( PEISCLASS( a ) ) { PElement c; if( (p = action_proc_dot_tag( rc, b, tag, 256 )) ) { if( !class_get_member( a, p, NULL, &c ) ) action_nomerror( rc, compile, a, b ); PEPUTPE( out, &c ); } else if( PEISSYMREF( b ) ) { if( !class_get_symbol( a, PEGETSYMREF( b ), &c ) ) action_nomerror( rc, compile, a, b ); PEPUTPE( out, &c ); } else action_boperror( rc, compile, _( "Bad right hand side of '.'." ), op, name, a, b ); } else if( PEISSYMREF( a ) ) { Symbol *sym = PEGETSYMREF( a ); Symbol *child; if( !is_scope( sym ) ) action_boperror( rc, compile, _( "Symbol on left hand side of '.' " "is not scope" ), op, name, a, b ); g_assert( sym->expr ); if( !(p = action_proc_dot_tag( rc, b, tag, 256 )) ) action_boperror( rc, compile, _( "Bad right hand side of '.'." ), op, name, a, b ); if( !(child = compile_lookup( sym->expr->compile, p )) ) action_nomerror( rc, compile, a, b ); /* Add all exprs which use compile to dynamic link graph. */ if( slist_map( compile->exprs, (SListMapFn) action_proc_dot_add_link, child ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); /* Don't check for dirty here ... wait for * link. */ if( child->type == SYM_VALUE ) { PEPUTP( out, ELEMENT_SYMBOL, child ); } else { PEPUTP( out, ELEMENT_SYMREF, child ); } } else if( PEISMANAGEDGOBJECT( a ) ) { GObject *gobject = PEGETMANAGEDGOBJECT( a ); GObjectClass *gclass = G_OBJECT_GET_CLASS( gobject ); GValue value = { 0 }; GParamSpec *pspec; if( !(p = action_proc_dot_tag( rc, b, tag, 256 )) ) action_boperror( rc, compile, _( "Bad right hand side of '.'." ), op, name, a, b ); if( !(pspec = g_object_class_find_property( gclass, p )) ) action_boperror( rc, compile, _( "Property not found." ), op, name, a, b ); g_value_init( &value, G_PARAM_SPEC_VALUE_TYPE( pspec ) ); g_object_get_property( gobject, p, &value); if( !heap_gvalue_to_ip( &value, out ) ) { g_value_unset( &value ); reduce_throw( rc ); } g_value_unset( &value ); } else action_boperror( rc, compile, _( "Bad left hand side of '.'." ), op, name, a, b ); } /* Less than or equal to. */ static void action_proc_lesseq( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { if( PEISREAL( a ) && PEISREAL( b ) ) { PEPUTP( out, ELEMENT_BOOL, PEGETREAL( a ) <= PEGETREAL( b ) ); } else if( PEISCHAR( a ) && PEISCHAR( b ) ) { PEPUTP( out, ELEMENT_BOOL, PEGETCHAR( a ) <= PEGETCHAR( b ) ); } else if( PEISLIST( a ) && PEISLIST( b ) && reduce_is_string( rc, a ) && reduce_is_string( rc, b ) ) { char a_string[MAX_STRSIZE]; char b_string[MAX_STRSIZE]; reduce_get_string( rc, a, a_string, MAX_STRSIZE ); reduce_get_string( rc, b, b_string, MAX_STRSIZE ); PEPUTP( out, ELEMENT_BOOL, strcmp( a_string, b_string ) <= 0 ); } else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_lesseqconst", PEGETII( a ), PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_moreeqconst", PEGETII( b ), PEGETREAL( a ) ); else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_lesseq", PEGETII( a ), PEGETII( b ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Strict less than. */ static void action_proc_less( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { if( PEISREAL( a ) && PEISREAL( b ) ) { PEPUTP( out, ELEMENT_BOOL, PEGETREAL( a ) < PEGETREAL( b ) ); } else if( PEISCHAR( a ) && PEISCHAR( b ) ) { PEPUTP( out, ELEMENT_BOOL, PEGETCHAR( a ) < PEGETCHAR( b ) ); } else if( PEISLIST( a ) && PEISLIST( b ) && reduce_is_string( rc, a ) && reduce_is_string( rc, b ) ) { char a_string[MAX_STRSIZE]; char b_string[MAX_STRSIZE]; reduce_get_string( rc, a, a_string, MAX_STRSIZE ); reduce_get_string( rc, b, b_string, MAX_STRSIZE ); PEPUTP( out, ELEMENT_BOOL, strcmp( a_string, b_string ) < 0 ); } else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_lessconst", PEGETII( a ), PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_moreconst", PEGETII( b ), PEGETREAL( a ) ); else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_less", PEGETII( a ), PEGETII( b ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Forward ref. */ static gboolean action_element_equal( Reduce *rc, PElement *a, PElement *b ); /* Test two nodes for equality. */ static gboolean action_node_equal( Reduce *rc, HeapNode *a, HeapNode *b ) { PElement la, ra; PElement lb, rb; /* Easy! */ if( a->type != b->type ) return( FALSE ); switch( a->type ) { case TAG_APPL: case TAG_CLASS: /* Function compare ... don't allow it. */ return( FALSE ); case TAG_CONS: /* Compare the elements. */ PEPOINTLEFT( a, &la ); PEPOINTLEFT( b, &lb ); PEPOINTRIGHT( a, &ra ); PEPOINTRIGHT( b, &rb ); return( action_element_equal( rc, &la, &lb ) && action_element_equal( rc, &ra, &rb ) ); case TAG_DOUBLE: return( a->body.num == b->body.num ); case TAG_COMPLEX: return( GETLEFT( a )->body.num == GETLEFT( b )->body.num && GETRIGHT( a )->body.num == GETRIGHT( b )->body.num ); case TAG_GEN: case TAG_FILE: case TAG_FREE: case TAG_SHARED: case TAG_REFERENCE: default: g_assert( FALSE ); /* Keep gcc happy. */ return( FALSE ); } } static gboolean action_image_equal( Reduce *rc, Imageinfo *a, Imageinfo *b ) { Imageinfo *ii[2]; IMAGE *t1; IMAGE *ai, *bi; gboolean use_luts; double mn; /* Easy tests first. */ ii[0] = a; ii[1] = b; g_assert( !ii[0] && !ii[1] ); if( ii[0] == ii[1] ) /* Trivial! */ return( TRUE ); /* Extract images ... get LUTs if the underlying image is the * same. */ use_luts = imageinfo_same_underlying( ii, 2 ); if( !(ai = imageinfo_get( use_luts, ii[0] )) || !(bi = imageinfo_get( use_luts, ii[1] )) ) { reduce_throw( rc ); /* Never get here, but keeps gcc happy. */ return( FALSE ); } /* Size and bands must be the same. */ if( ai->Xsize != bi->Xsize || ai->Ysize != bi->Ysize || ai->Bands != bi->Bands || ai->Coding != bi->Coding ) return( FALSE ); /* Exhaustive test. */ if( !(t1 = im_open( "equals:1", "p" )) ) { error_vips_all(); reduce_throw( rc ); } if( im_equal( ai, bi, t1 ) || im_min( t1, &mn ) ) { im_close( t1 ); error_vips_all(); reduce_throw( rc ); } im_close( t1 ); return( mn == 255 ); } /* One of p1/p2 is a managedstring. * * This is pretty dumb. We could have a special loop down the list side which * compared to the managedstring directly, but I doubt if this will ever be a * performance issue. */ static gboolean action_string_equal( Reduce *rc, PElement *p1, PElement *p2 ) { char a_string[MAX_STRSIZE]; char b_string[MAX_STRSIZE]; reduce_get_string( rc, p1, a_string, MAX_STRSIZE ); reduce_get_string( rc, p2, b_string, MAX_STRSIZE ); return( strcmp( a_string, b_string ) == 0 ); } /* Test two elements for equality. Force computation as required. */ static gboolean action_element_equal( Reduce *rc, PElement *p1, PElement *p2 ) { /* Reduce a bit. */ reduce_spine( rc, p1 ); reduce_spine( rc, p2 ); /* We can often test for eg. "fred" == "fred" by just checking * pointers. */ if( PEGETTYPE( p1 ) == PEGETTYPE( p2 ) ) { switch( PEGETTYPE( p1 ) ) { case ELEMENT_CHAR: case ELEMENT_NODE: case ELEMENT_BOOL: case ELEMENT_MANAGED: if( PEGETVAL( p1 ) == PEGETVAL( p2 ) ) return( TRUE ); break; case ELEMENT_ELIST: return( TRUE ); } } /* Special case if either is a managedstring. */ if( PEISMANAGEDSTRING( p1 ) || PEISMANAGEDSTRING( p2 ) ) return( action_string_equal( rc, p1, p2 ) ); /* No other implicit conversions, so types must match. */ if( PEGETTYPE( p1 ) != PEGETTYPE( p2 ) ) return( FALSE ); switch( PEGETTYPE( p1 ) ) { case ELEMENT_TAG: case ELEMENT_BINOP: case ELEMENT_UNOP: case ELEMENT_SYMBOL: case ELEMENT_SYMREF: case ELEMENT_COMPILEREF: case ELEMENT_CONSTRUCTOR: case ELEMENT_COMB: /* Don't allow function compare. */ return( FALSE ); case ELEMENT_NODE: /* Compare the HeapNodes. */ return( action_node_equal( rc, PEGETVAL( p1 ), PEGETVAL( p2 ) ) ); case ELEMENT_CHAR: return( PEGETCHAR( p1 ) == PEGETCHAR( p2 ) ); case ELEMENT_BOOL: return( PEGETBOOL( p1 ) == PEGETBOOL( p2 ) ); case ELEMENT_MANAGED: if( PEISIMAGE( p1 ) && PEISIMAGE( p2 ) ) return( action_image_equal( rc, PEGETII( p1 ), PEGETII( p2 ) ) ); else return( FALSE ); case ELEMENT_ELIST: return( TRUE ); case ELEMENT_NOVAL: default: g_assert( FALSE ); /* Keep gcc happy. */ return( FALSE ); } } /* Top-level == ... special form for image args. */ static void action_proc_equal( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out ) { gboolean res; PElement left, right; PElement *a = &left; PElement *b = &right; PEPOINTRIGHT( arg[1], &left ); PEPOINTRIGHT( arg[0], &right ); if( PEISIMAGE( a ) || PEISIMAGE( b ) ) { /* Special image equals form. Hyperstrict. */ reduce_spine_strict( rc, a ); reduce_spine_strict( rc, b ); if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_equalconst", PEGETII( a ), PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_equalconst", PEGETII( b ), PEGETREAL( a ) ); else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_equal", PEGETII( a ), PEGETII( b ) ); else PEPUTP( out, ELEMENT_BOOL, FALSE ); } else { /* Lazy form. */ res = action_element_equal( rc, a, b ); PEPUTP( out, ELEMENT_BOOL, res ); } } /* Top-level != ... special form for image args. */ static void action_proc_notequal( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out ) { gboolean res; PElement left, right; PElement *a = &left; PElement *b = &right; PEPOINTRIGHT( arg[1], &left ); PEPOINTRIGHT( arg[0], &right ); if( PEISIMAGE( a ) || PEISIMAGE( b ) ) { /* Special image equals form. Hyperstrict. */ reduce_spine_strict( rc, a ); reduce_spine_strict( rc, b ); if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_notequalconst", PEGETII( a ), PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_notequalconst", PEGETII( b ), PEGETREAL( a ) ); else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_notequal", PEGETII( a ), PEGETII( b ) ); else PEPUTP( out, ELEMENT_BOOL, TRUE ); } else { res = action_element_equal( rc, a, b ); PEPUTP( out, ELEMENT_BOOL, !res ); } } static void * action_proc_join_sub( Reduce *rc, PElement *pe, PElement *a, PElement *b, PElement *out ) { if( !heap_list_cat( rc, a, b, pe ) ) return( a ); PEPUTPE( out, pe ); return( NULL ); } static void action_proc_join( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out ) { PElement left, right; PElement *a = &left; PElement *b = &right; PEPOINTRIGHT( arg[1], &left ); PEPOINTRIGHT( arg[0], &right ); if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_bandjoin", PEGETII( a ), PEGETII( b ) ); else if( PEISLIST( a ) && PEISLIST( b ) ) { if( reduce_safe_pointer( rc, (reduce_safe_pointer_fn) action_proc_join_sub, a, b, out, NULL ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISELIST( b ) ) { PEPUTPE( out, a ); } else if( PEISIMAGE( b ) && PEISELIST( a ) ) { PEPUTPE( out, b ); } else action_boperror( rc, compile, NULL, op, name, a, b ); } static void action_proc_index( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out ) { PElement left, right; PElement *a = &left; PElement *b = &right; PEPOINTRIGHT( arg[1], &left ); PEPOINTRIGHT( arg[0], &right ); if( PEISLIST( a ) && PEISREAL( b ) ) { PElement result; reduce_list_index( rc, a, PEGETREAL( b ), &result ); PEPUTPE( out, &result ); } else if( PEISIMAGE( a ) && PEISREAL( b ) ) { callva( rc, out, "im_extract_band", PEGETII( a ), (int) PEGETREAL( b ) ); } else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Raise to power. */ static void action_proc_exp( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { if( !heap_real_new( heap, pow( PEGETREAL( a ), PEGETREAL( b ) ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_powtra", PEGETII( a ), PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_expntra", PEGETII( b ), PEGETREAL( a ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Left shift. */ static void action_proc_lshift( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { int v1 = PEGETREAL( a ); int v2 = PEGETREAL( b ); if( !heap_real_new( heap, v1 << v2, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_shiftleft", PEGETII( a ), (int) PEGETREAL( b ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Right shift. */ static void action_proc_rshift( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { int v1 = PEGETREAL( a ); int v2 = PEGETREAL( b ); if( !heap_real_new( heap, v1 >> v2, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_shiftright", PEGETII( a ), (int) PEGETREAL( b ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Remainder. */ static void action_proc_rem( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { int v1 = PEGETREAL( a ); int v2 = PEGETREAL( b ); if( v2 == 0 ) action_boperror( rc, compile, _( "Division by zero." ), op, name, a, b ); if( !heap_real_new( heap, v1 % v2, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_remainder", PEGETII( a ), PEGETII( b ) ); else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_remainderconst", PEGETII( a ), PEGETREAL( b ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Divide two objects. */ static void action_proc_div( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { if( !heap_real_new( heap, PEGETREAL( a ) / PEGETREAL( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISCOMPLEX( b ) ) { double x1 = PEGETREALPART( a ); double y1 = PEGETIMAGPART( a ); double x2 = PEGETREALPART( b ); double y2 = PEGETIMAGPART( b ); if( !heap_complex_new( heap, (x1 * x2 + y1 * y2) / (x2 * x2 + y2 * y2), (y1 * x2 - x1 * y2) / (x2 * x2 + y2 * y2), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISREAL( b ) ) { double x1 = PEGETREALPART( a ); double y1 = PEGETIMAGPART( a ); double x2 = PEGETREAL( b ); if( !heap_complex_new( heap, (x1 * x2) / (x2 * x2), (y1 * x2) / (x2 * x2), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISREAL( a ) && PEISCOMPLEX( b ) ) { double x1 = PEGETREAL( a ); double x2 = PEGETREALPART( b ); double y2 = PEGETIMAGPART( b ); if( !heap_complex_new( heap, (x1 * x2) / (x2 * x2 + y2 * y2), (-x1 * y2) / (x2 * x2 + y2 * y2), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_divide", PEGETII( a ), PEGETII( b ) ); else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_lintra", 1.0 / PEGETREAL( b ), PEGETII( a ), 0.0 ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) { HeapNode hn; PElement rhs; /* Use this for intermediates. */ PEPOINTRIGHT( &hn, &rhs ); /* Take recip. */ callva( rc, &rhs, "im_powtra", PEGETII( b ), -1.0 ); /* Now multiply by const. */ callva( rc, out, "im_lintra", PEGETREAL( a ), PEGETII( &rhs ), 0.0 ); } else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Multiply two objects. */ static void action_proc_mul( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { if( !heap_real_new( heap, PEGETREAL( a ) * PEGETREAL( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISCOMPLEX( b ) ) { double x1 = PEGETREALPART( a ); double y1 = PEGETIMAGPART( a ); double x2 = PEGETREALPART( b ); double y2 = PEGETIMAGPART( b ); if( !heap_complex_new( heap, x1*x2 - y1*y2, x1*y2 + x2*y1, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISREAL( b ) ) { if( !heap_complex_new( heap, PEGETREALPART( a ) * PEGETREAL( b ), PEGETIMAGPART( a ) * PEGETREAL( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISREAL( a ) && PEISCOMPLEX( b ) ) { if( !heap_complex_new( heap, PEGETREAL( a ) * PEGETREALPART( b ), PEGETREAL( a ) * PEGETIMAGPART( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_multiply", PEGETII( a ), PEGETII( b ) ); else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_lintra", PEGETREAL( b ), PEGETII( a ), 0.0 ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_lintra", PEGETREAL( a ), PEGETII( b ), 0.0 ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Subtract two objects. */ static void action_proc_sub( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { if( !heap_real_new( heap, PEGETREAL( a ) - PEGETREAL( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISCOMPLEX( b ) ) { if( !heap_complex_new( heap, PEGETREALPART( a ) - PEGETREALPART( b ), PEGETIMAGPART( a ) - PEGETIMAGPART( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISREAL( b ) ) { if( !heap_complex_new( heap, PEGETREALPART( a ) - PEGETREAL( b ), PEGETIMAGPART( a ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISREAL( a ) && PEISCOMPLEX( b ) ) { if( !heap_complex_new( heap, PEGETREAL( a ) - PEGETREALPART( b ), PEGETIMAGPART( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_subtract", PEGETII( a ), PEGETII( b ) ); else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_lintra", 1.0, PEGETII( a ), -PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_lintra", -1.0, PEGETII( b ), PEGETREAL( a ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Add two objects. */ static void action_proc_add( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { Heap *heap = rc->heap; if( PEISREAL( a ) && PEISREAL( b ) ) { if( !heap_real_new( heap, PEGETREAL( a ) + PEGETREAL( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISCOMPLEX( b ) ) { if( !heap_complex_new( heap, PEGETREALPART( a ) + PEGETREALPART( b ), PEGETIMAGPART( a ) + PEGETIMAGPART( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISCOMPLEX( a ) && PEISREAL( b ) ) { if( !heap_complex_new( heap, PEGETREALPART( a ) + PEGETREAL( b ), PEGETIMAGPART( a ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISREAL( a ) && PEISCOMPLEX( b ) ) { if( !heap_complex_new( heap, PEGETREAL( a ) + PEGETREALPART( b ), PEGETIMAGPART( b ), out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_add", PEGETII( a ), PEGETII( b ) ); else if( PEISIMAGE( a ) && PEISREAL( b ) ) callva( rc, out, "im_lintra", 1.0, PEGETII( a ), PEGETREAL( b ) ); else if( PEISREAL( a ) && PEISIMAGE( b ) ) callva( rc, out, "im_lintra", 1.0, PEGETII( b ), PEGETREAL( a ) ); else action_boperror( rc, compile, NULL, op, name, a, b ); } /* Evaluate a binary operator on args a and b, write the result to out. a and * b already reduced. * * Call one of the things above. Not all combinations implemented, got bored :/ * Implement simple things in the switch, break to the functions above for * the rest. * * out can be rhs of argv[0], careful. */ static void action_proc_bop_strict( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out ) { Heap *heap = rc->heap; HeapNode *hn; PElement left, right; PElement *a = &left; PElement *b = &right; PEPOINTRIGHT( arg[1], &left ); PEPOINTRIGHT( arg[0], &right ); switch( op ) { case BI_SELECT: action_proc_index( rc, compile, op, name, arg, out ); break; case BI_JOIN: action_proc_join( rc, compile, op, name, arg, out ); break; case BI_EQ: action_proc_equal( rc, compile, op, name, arg, out ); break; case BI_NOTEQ: action_proc_notequal( rc, compile, op, name, arg, out ); break; case BI_PEQ: PEPUTP( out, ELEMENT_BOOL, PEGETTYPE( a ) == PEGETTYPE( b ) && PEGETVAL( a ) == PEGETVAL( b ) ); break; case BI_PNOTEQ: PEPUTP( out, ELEMENT_BOOL, PEGETTYPE( a ) != PEGETTYPE( b ) || PEGETVAL( a ) != PEGETVAL( b ) ); break; case BI_ADD: action_proc_add( rc, compile, op, name, a, b, out ); break; case BI_SUB: action_proc_sub( rc, compile, op, name, a, b, out ); break; case BI_MUL: action_proc_mul( rc, compile, op, name, a, b, out ); break; case BI_DIV: action_proc_div( rc, compile, op, name, a, b, out ); break; case BI_DOT: action_proc_dot( rc, compile, op, name, a, b, out ); break; case BI_POW: action_proc_exp( rc, compile, op, name, a, b, out ); break; case BI_LSHIFT: action_proc_lshift( rc, compile, op, name, a, b, out ); break; case BI_RSHIFT: action_proc_rshift( rc, compile, op, name, a, b, out ); break; case BI_REM: action_proc_rem( rc, compile, op, name, a, b, out ); break; case BI_LESS: action_proc_less( rc, compile, op, name, a, b, out ); break; case BI_LESSEQ: action_proc_lesseq( rc, compile, op, name, a, b, out ); break; case BI_COMMA: if( PEISREAL( a ) && PEISREAL( b ) ) { if( NEWNODE( heap, hn ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); /* Form complex node. */ hn->type = TAG_COMPLEX; PPUT( hn, ELEMENT_NODE, PEGETVAL( a ), ELEMENT_NODE, PEGETVAL( b ) ); PEPUTP( out, ELEMENT_NODE, hn ); } else if( PEISIMAGE( a ) && PEISIMAGE( b ) ) { callva( rc, out, "im_ri2c", PEGETII( a ), PEGETII( b ) ); } else action_boperror( rc, compile, NULL, op, name, a, b ); break; case BI_BAND: action_proc_band( rc, compile, op, name, a, b, out ); break; case BI_BOR: action_proc_bor( rc, compile, op, name, a, b, out ); break; case BI_EOR: action_proc_eor( rc, compile, op, name, a, b, out ); break; case BI_NONE: default: action_boperror( rc, compile, _( "Unimplemented." ), op, name, a, b ); break; } } /* Evaluate a unary operator on arg a, write the result to out. a * already reduced. */ void action_proc_uop( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out ) { Heap *heap = rc->heap; PElement pe, *a = &pe; PElement rhs; PEPOINTRIGHT( arg[0], &pe ); switch( op ) { case UN_NEG: if( PEISREAL( a ) ) { if( !heap_real_new( heap, !PEGETREAL( a ), out ) ) reduce_throw( rc ); } else if( PEISCOMPLEX( a ) ) { if( !heap_complex_new( heap, !PEGETREALPART( a ), !PEGETIMAGPART( a ), out ) ) reduce_throw( rc ); } else if( PEISBOOL( a ) ) { PEPUTP( out, ELEMENT_BOOL, !PEGETBOOL( a ) ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_equalconst", PEGETII( a ), 0.0 ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_MINUS: if( PEISREAL( a ) ) { if( !heap_real_new( heap, -PEGETREAL( a ), out ) ) reduce_throw( rc ); } else if( PEISCOMPLEX( a ) ) { if( !heap_complex_new( heap, -PEGETREALPART( a ), -PEGETIMAGPART( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_lintra", -1.0, PEGETII( a ), 0.0 ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_COMPLEMENT: if( PEISREAL( a ) ) { int v = PEGETREAL( a ); if( !heap_real_new( heap, ~v, out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_eorimageconst", PEGETII( a ), -1 ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_PLUS: PEPUTPE( out, a ); break; case UN_CSCHAR: /* Convert to signed char. */ if( PEISNUM( a ) ) { action_set_range( rc, SCHAR_MIN, SCHAR_MAX, a, out ); } else if( PEISCHAR( a ) ) { if( !heap_real_new( heap, PEGETCHAR( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip2c", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CUCHAR: /* Convert to unsigned char, eg. 65 => 'A'. */ if( PEISREAL( a ) ) { double v = PEGETREAL( a ); v = IM_CLIP( 0, v, UCHAR_MAX ); PEPUTP( out, ELEMENT_CHAR, (int) v ); } else if( PEISCOMPLEX( a ) ) { double v = PEGETREALPART( a ); v = IM_CLIP( 0, v, UCHAR_MAX ); PEPUTP( out, ELEMENT_CHAR, (int) v ); } else if( PEISCHAR( a ) ) { PEPUTPE( out, a ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CSINT: /* Convert to signed int. */ if( PEISNUM( a ) ) action_set_range( rc, INT_MIN, INT_MAX, a, out ); else if( PEISCHAR( a ) ) { if( !heap_real_new( heap, PEGETCHAR( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip2i", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CUINT: /* Convert to unsigned int. */ if( PEISREAL( a ) || PEISCOMPLEX( a ) ) action_set_range( rc, 0, UINT_MAX, a, out ); else if( PEISCHAR( a ) ) { if( !heap_real_new( heap, PEGETCHAR( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip2ui", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CSSHORT: /* Convert to signed short. */ if( PEISREAL( a ) || PEISCOMPLEX( a ) ) action_set_range( rc, SHRT_MIN, SHRT_MAX, a, out ); else if( PEISCHAR( a ) ) { if( !heap_real_new( heap, PEGETCHAR( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip2s", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CUSHORT: /* Convert to unsigned short. */ if( PEISREAL( a ) || PEISCOMPLEX( a ) ) action_set_range( rc, 0, USHRT_MAX, a, out ); else if( PEISCHAR( a ) ) { if( !heap_real_new( heap, PEGETCHAR( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip2us", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CFLOAT: /* Convert to float ... just drop imag part. */ if( PEISCOMPLEX( a ) ) { if( !heap_real_new( heap, PEGETREALPART( a ), out ) ) reduce_throw( rc ); } else if( PEISREAL( a ) ) { PEPUTPE( out, a ); } else if( PEISCHAR( a ) ) { if( !heap_real_new( heap, PEGETCHAR( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip2f", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CDOUBLE: /* Convert to double ... just drop imag part. */ if( PEISCOMPLEX( a ) ) { if( !heap_real_new( heap, PEGETREALPART( a ), out ) ) reduce_throw( rc ); } else if( PEISREAL( a ) ) { PEPUTPE( out, a ); } else if( PEISCHAR( a ) ) { if( !heap_real_new( heap, PEGETCHAR( a ), out ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) callva( rc, out, "im_clip2d", PEGETII( a ) ); else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_CDCOMPLEX: case UN_CCOMPLEX: /* Convert to complex ... set imag = 0. */ if( PEISREAL( a ) ) { /* Make base node. */ if( !heap_complex_element_new( heap, a, a, out ) ) reduce_throw( rc ); /* Install new imag part. */ PEPOINTRIGHT( PEGETVAL( out ), &rhs ); if( !heap_real_new( heap, 0, &rhs ) ) reduce_throw( rc ); } else if( PEISCOMPLEX( a ) ) { PEPUTPE( out, a ); } else if( PEISCHAR( a ) ) { /* Make base node. */ if( !heap_complex_element_new( heap, a, a, out ) ) reduce_throw( rc ); /* Install new real and imag parts. */ PEPOINTLEFT( PEGETVAL( out ), &rhs ); if( !heap_real_new( heap, PEGETCHAR( a ), &rhs ) ) reduce_throw( rc ); PEPOINTRIGHT( PEGETVAL( out ), &rhs ); if( !heap_real_new( heap, 0, &rhs ) ) reduce_throw( rc ); } else if( PEISIMAGE( a ) ) { if( op == UN_CCOMPLEX ) callva( rc, out, "im_clip2cm", PEGETII( a ) ); else callva( rc, out, "im_clip2dcm", PEGETII( a ) ); } else action_uoperror( rc, compile, NULL, op, name, a ); break; case UN_NONE: default: action_uoperror( rc, compile, _( "Unimplemented." ), op, name, a ); break; } } static void * action_proc_construct_sub( Reduce *rc, PElement *pe, Compile *compile, HeapNode **arg, PElement *out ) { if( !class_new( rc->heap, compile, arg, pe ) ) reduce_throw( rc ); PEPUTPE( out, pe ); return( NULL ); } /* Eval a constructor. Nasty: out can be RHS of arg[0], so we have to build * instance in a safe spot, then write back to out afterwards. */ void action_proc_construct( Reduce *rc, Compile *compile, HeapNode **arg, PElement *out ) { if( trace_flags & TRACE_CLASS_NEW ) { VipsBuf *buf = trace_push(); vips_buf_appendf( buf, "constructor \"%s\" ", IOBJECT( compile->sym )->name ); trace_args( arg, compile->nparam + compile->nsecret ); } if( reduce_safe_pointer( rc, (reduce_safe_pointer_fn) action_proc_construct_sub, compile, arg, out, NULL ) ) reduce_throw( rc ); /* Is it a class with a typecheck member? Return that instead. */ if( compile_lookup( compile, MEMBER_CHECK ) && class_get_member( out, MEMBER_CHECK, NULL, out ) ) { #ifdef DEBUG printf( "reduce: invoking arg checker\n" ); #endif } if( trace_flags & TRACE_CLASS_NEW ) { trace_result( TRACE_CLASS_NEW, out ); trace_pop(); } } static void * action_proc_class_binary_sub( Reduce *rc, PElement *pe, PElement *fn, const char *name, PElement *b, PElement *out ) { PElement rhs; PElement base; base = *pe; heap_appl_init( &base, fn ); if( !heap_appl_add( rc->heap, &base, &rhs ) || !heap_managedstring_new( rc->heap, name, &rhs ) || !heap_appl_add( rc->heap, &base, &rhs ) ) return( out ); PEPUTPE( &rhs, b ); PEPUTPE( out, pe ); return( NULL ); } /* Something like "class + 12" ... call (class.add 12) */ static void action_proc_class_binary( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { TraceFlags flags = op >= 0 ? TRACE_OPERATOR : TRACE_BUILTIN; PElement fn; if( trace_flags & flags ) { VipsBuf *buf = trace_push(); vips_buf_appendf( buf, "%s\n", _( "invoking method:" ) ); vips_buf_appends( buf, " " ); trace_pelement( a ); vips_buf_appendf( buf, ".%s \"%s\" ", MEMBER_OO_BINARY, name ); trace_pelement( b ); vips_buf_appends( buf, "\n" ); trace_text( flags, "%s", vips_buf_all( buf ) ); trace_pop(); } /* Look up a.oo_binary and build (a.dispatch_binary "add" b) * application. */ if( !class_get_member( a, MEMBER_OO_BINARY, NULL, &fn ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); if( reduce_safe_pointer( rc, (reduce_safe_pointer_fn) action_proc_class_binary_sub, &fn, (void *) name, b, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } /* Something like "12 + class" ... call (class.add' 12) */ static void action_proc_class_binary2( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { TraceFlags flags = op >= 0 ? TRACE_OPERATOR : TRACE_BUILTIN; PElement fn; if( trace_flags & flags ) { VipsBuf *buf = trace_push(); vips_buf_appendf( buf, "%s\n", _( "invoking method:" ) ); vips_buf_appends( buf, " " ); trace_pelement( b ); vips_buf_appendf( buf, ".%s \"%s\" ", MEMBER_OO_BINARY2, name ); trace_pelement( a ); vips_buf_appends( buf, "\n" ); trace_text( flags, "%s", vips_buf_all( buf ) ); trace_pop(); } /* Look up b.dispatch_binary2 and build * (b.dispatch_binary2 "add" a) application. */ if( !class_get_member( b, MEMBER_OO_BINARY2, NULL, &fn ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); if( reduce_safe_pointer( rc, (reduce_safe_pointer_fn) action_proc_class_binary_sub, &fn, (void *) name, a, out ) ) action_boperror( rc, compile, error_get_sub(), op, name, a, b ); } static void action_landlor( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { reduce_spine( rc, a ); if( PEISCOMB( a ) && PEGETCOMB( a ) == COMB_I ) /* The reduce_spine() did us recursively ... bounce back. */ return; if( trace_flags & TRACE_OPERATOR ) trace_push(); /* Examine the LHS and see if we can avoid RHS eval. */ if( PEISCLASS( a ) ) action_proc_class_binary( rc, compile, op, name, a, b, out ); else if( PEISBOOL( a ) ) { if( op == BI_LOR && PEGETBOOL( a ) ) { if( trace_flags & TRACE_OPERATOR ) trace_binop( compile, a, op, b ); PEPUTP( out, ELEMENT_BOOL, TRUE ); if( trace_flags & TRACE_OPERATOR ) trace_result( TRACE_OPERATOR, out ); } else if( op == BI_LAND && !PEGETBOOL( a ) ) { if( trace_flags & TRACE_OPERATOR ) trace_binop( compile, a, op, b ); PEPUTP( out, ELEMENT_BOOL, FALSE ); if( trace_flags & TRACE_OPERATOR ) trace_result( TRACE_OPERATOR, out ); } else { /* Need to look at RHS too. */ reduce_spine( rc, b ); if( PEISCOMB( b ) && PEGETCOMB( b ) != COMB_I ) { if( trace_flags & TRACE_OPERATOR ) trace_pop(); return; } if( PEISCLASS( b ) ) action_proc_class_binary2( rc, compile, op, name, a, b, out ); else if( PEISBOOL( b ) ) { if( trace_flags & TRACE_OPERATOR ) trace_binop( compile, a, op, b ); PEPUTP( out, ELEMENT_BOOL, PEGETBOOL( b ) ); if( trace_flags & TRACE_OPERATOR ) trace_result( TRACE_OPERATOR, out ); } else action_boperror( rc, compile, NULL, op, name, a, b ); } } else action_boperror( rc, compile, NULL, op, name, a, b ); if( trace_flags & TRACE_OPERATOR ) trace_pop(); } static void action_if( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *b, PElement *out ) { reduce_spine( rc, a ); if( PEISCOMB( a ) && PEGETCOMB( a ) == COMB_I ) /* The reduce_spine() did us recursively ... bounce back. */ return; if( PEISCLASS( a ) ) action_proc_class_binary( rc, compile, op, name, a, b, out ); else { PElement t, e; /* a is condition, b should be [then-part, else-part] ... * look down b and find them. Block trace for this, not very * interesting. */ trace_block(); reduce_list_index( rc, b, 0, &t ); reduce_list_index( rc, b, 1, &e ); trace_unblock(); /* Can be BOOL or image. */ if( PEISBOOL( a ) ) { if( trace_flags & TRACE_OPERATOR ) { VipsBuf *buf = trace_push(); vips_buf_appendf( buf, "if " ); trace_pelement( a ); vips_buf_appendf( buf, " then " ); trace_pelement( &t ); vips_buf_appendf( buf, " else " ); trace_pelement( &e ); vips_buf_appendf( buf, " ->\n" ); } if( PEGETBOOL( a ) ) { PEPUTPE( out, &t ); } else { PEPUTPE( out, &e ); } if( trace_flags & TRACE_OPERATOR ) { trace_result( TRACE_OPERATOR, out ); trace_pop(); } } else if( PEISIMAGE( a ) ) { reduce_spine_strict( rc, &t ); reduce_spine_strict( rc, &e ); /* then/else parts must both be image. */ if( !PEISIMAGE( &t ) || !PEISIMAGE( &e ) ) action_boperror( rc, compile, NULL, op, name, a, b ); callva( rc, out, "im_ifthenelse", PEGETII( a ), PEGETII( &t ), PEGETII( &e ) ); } else action_boperror( rc, compile, NULL, op, name, a, b ); } } /* Do a binary operator. Result in arg[0]. */ void action_proc_bop( Reduce *rc, Compile *compile, BinOp bop, HeapNode **arg ) { PElement a, b, out; switch( bop ) { case BI_LAND: case BI_LOR: /* Special ninja magic :-( we need to handle reduce carefully * here. */ PEPOINTRIGHT( arg[0], &b ); PEPOINTRIGHT( arg[1], &a ); PEPOINTRIGHT( arg[0], &out ); action_landlor( rc, compile, bop, OPERATOR_NAME( bop ), &a, &b, &out ); /* Overwrite arg[0] with I node, in case this is a * shared node. */ PPUTLEFT( arg[0], ELEMENT_COMB, COMB_I ); break; case BI_IF: /* If is lazy-ish in it's 2nd argument too. */ PEPOINTRIGHT( arg[0], &b ); PEPOINTRIGHT( arg[1], &a ); PEPOINTRIGHT( arg[0], &out ); action_if( rc, compile, bop, OPERATOR_NAME( bop ), &a, &b, &out ); /* Overwrite arg[0] with I node, in case this is a * shared node. */ PPUTLEFT( arg[0], ELEMENT_COMB, COMB_I ); break; case BI_DOT: case BI_PEQ: case BI_PNOTEQ: /* Strict, not overrideable. */ action_dispatch( rc, compile, reduce_spine, bop, OPERATOR_NAME( bop ), FALSE, (ActionFn) action_proc_bop_strict, 2, arg, NULL ); break; default: /* Strict, overrideable. */ action_dispatch( rc, compile, reduce_spine, bop, OPERATOR_NAME( bop ), TRUE, (ActionFn) action_proc_bop_strict, 2, arg, NULL ); } } static void * action_proc_class_unary_sub( Reduce *rc, PElement *pe, PElement *fn, const char *name, PElement *out ) { PElement rhs; PElement base; base = *pe; heap_appl_init( &base, fn ); if( !heap_appl_add( rc->heap, &base, &rhs ) || !heap_managedstring_new( rc->heap, name, &rhs ) ) return( out ); PEPUTPE( out, pe ); return( NULL ); } static void action_proc_class_unary( Reduce *rc, Compile *compile, int op, const char *name, PElement *a, PElement *out ) { TraceFlags flags = op >= 0 ? TRACE_OPERATOR : TRACE_BUILTIN; PElement fn; if( trace_flags & flags ) { VipsBuf *buf = trace_push(); vips_buf_appendf( buf, "%s\n", _( "invoking method:" ) ); vips_buf_appends( buf, " " ); trace_pelement( a ); vips_buf_appendf( buf, ".%s \"%s\"\n", MEMBER_OO_UNARY, name ); trace_text( flags, "%s", vips_buf_all( buf ) ); trace_pop(); } /* Look up a.dispatch_unary and build * (a.oo_unary "minus") application. */ if( !class_get_member( a, MEMBER_OO_UNARY, NULL, &fn ) ) action_uoperror( rc, compile, error_get_sub(), op, name, a ); if( reduce_safe_pointer( rc, (reduce_safe_pointer_fn) action_proc_class_unary_sub, &fn, (void *) name, out, NULL ) ) action_uoperror( rc, compile, error_get_sub(), op, name, a ); } /* Run a function on the graph ... eval all the args, avoid eval if we reduce * ourselves as a side effect (happens on recursive calls). Result in RHS of * arg[0]. */ void action_dispatch( Reduce *rc, Compile *compile, ReduceFunction rfn, int op, const char *name, gboolean override, ActionFn afn, int nargs, HeapNode **arg, void *user ) { TraceFlags flags = op >= 0 ? TRACE_OPERATOR : TRACE_BUILTIN; PElement a, b; int i; /* Don't allow nargs == 0. We rely on having a bit of graph we can * replace with (I result) for caching. */ g_assert( nargs > 0 ); /* We need to have the */ g_assert( noperator_table == UN_LAST ); /* Reduce all the args. */ for( i = 0; i < nargs; i++ ) { PElement rhs; PEPOINTRIGHT( arg[i], &rhs ); rfn( rc, &rhs ); } /* We may have evaled ourselves already. */ PEPOINTLEFT( arg[0], &b ); if( PEISCOMB( &b ) && PEGETCOMB( &b ) == COMB_I ) return; PEPOINTRIGHT( arg[0], &b ); PEPOINTRIGHT( arg[1], &a ); if( trace_flags & flags ) { VipsBuf *buf = trace_push(); vips_buf_appendf( buf, "\"%s\" ", name ); trace_args( arg, nargs ); } if( override && nargs == 2 && PEISCLASS( &a ) ) action_proc_class_binary( rc, compile, op, name, &a, &b, &b ); else if( override && nargs == 2 && PEISCLASS( &b ) ) action_proc_class_binary2( rc, compile, op, name, &a, &b, &b ); else if( override && nargs == 1 && PEISCLASS( &b ) ) action_proc_class_unary( rc, compile, op, name, &b, &b ); else afn( rc, compile, op, name, arg, &b, user ); PPUTLEFT( arg[0], ELEMENT_COMB, COMB_I ); if( trace_flags & flags ) { trace_result( flags, &b ); trace_pop(); } } ================================================ FILE: src/action.h ================================================ /* Graph actions. */ /* A strict action on the graph. */ typedef void (*ActionFn)( Reduce *, Compile *, int, const char *, HeapNode **, PElement *, void * ); /* A sort of reducer (eg. lazy or strict, or hyperstrict) */ typedef void (*ReduceFunction)( Reduce *, PElement * ); #define OPERATOR_NAME( OP ) ( \ (int) (OP) >= 0 && (int) (OP) < noperator_table ? \ operator_table[(int) (OP)] : "" \ ) extern const char *operator_table[]; extern const int noperator_table; void action_proc_uop( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out ); void action_proc_construct( Reduce *rc, Compile *compile, HeapNode **arg, PElement *out ); void action_proc_bop( Reduce *rc, Compile *compile, BinOp bop, HeapNode **arg ); void action_dispatch( Reduce *rc, Compile *compile, ReduceFunction rfn, int op, const char *name, gboolean override, ActionFn afn, int nargs, HeapNode **arg, void *user ); ================================================ FILE: src/boxes.c ================================================ /* Make various little popup dialogs ... error, info, question. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Max amount of text in a info/error/question dialog. */ #define MAX_DIALOG_TEXT (2000) /* Find a window to use as dialog parent. */ static GtkWidget * box_pick_parent( GtkWidget *par ) { if( !par ) return( GTK_WIDGET( mainw_pick_one() ) ); else return( par ); } /* Make the insides of a error, info or question dialog. */ static void box_build( iDialog *idlg, GtkWidget *work, char *s, const char *stock_id ) { GtkWidget *icon; GtkWidget *hb; GtkWidget *lab; hb = gtk_hbox_new( FALSE, 12 ); gtk_container_border_width( GTK_CONTAINER( hb ), 0 ); gtk_container_add( GTK_CONTAINER( work ), hb ); gtk_widget_show( hb ); icon = gtk_image_new_from_stock( stock_id, GTK_ICON_SIZE_DIALOG ); gtk_misc_set_alignment( GTK_MISC( icon ), 0.0, 0.0 ); gtk_box_pack_start( GTK_BOX( hb ), icon, FALSE, FALSE, 0 ); gtk_widget_show( icon ); lab = gtk_label_new( NULL ); gtk_label_set_markup( GTK_LABEL( lab ), s ); gtk_label_set_justify( GTK_LABEL( lab ), GTK_JUSTIFY_LEFT ); gtk_label_set_selectable( GTK_LABEL( lab ), TRUE ); gtk_label_set_line_wrap( GTK_LABEL( lab ), TRUE ); gtk_box_pack_start( GTK_BOX( hb ), lab, FALSE, FALSE, 0 ); gtk_widget_show( lab ); } /* Make an error dialog. */ /*VARARGS2*/ static void box_error( GtkWidget *par, const char *fmt, ... ) { va_list ap; char buf[MAX_DIALOG_TEXT]; GtkWidget *idlg; va_start( ap, fmt ); (void) im_vsnprintf( buf, MAX_DIALOG_TEXT, fmt, ap ); va_end( ap ); idlg = idialog_new(); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) box_build, buf, GTK_STOCK_DIALOG_ERROR, NULL ); idialog_set_callbacks( IDIALOG( idlg ), NULL, NULL, NULL, NULL ); idialog_add_ok( IDIALOG( idlg ), iwindow_true_cb, GTK_STOCK_OK ); iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } /* Mark up a top/sub pair for a dialog box. */ static void box_vmarkup( char *out, const char *top, const char *sub, va_list ap ) { char buf1[MAX_DIALOG_TEXT]; char buf2[MAX_DIALOG_TEXT]; char buf3[MAX_DIALOG_TEXT]; escape_markup( top, buf1, MAX_DIALOG_TEXT ); (void) im_vsnprintf( buf2, MAX_DIALOG_TEXT, sub, ap ); escape_markup( buf2, buf3, MAX_DIALOG_TEXT ); (void) im_snprintf( out, MAX_DIALOG_TEXT, "%s", buf1 ); if( strcmp( buf3, "" ) != 0 ) { int len = strlen( out ); (void) im_snprintf( out + len, MAX_DIALOG_TEXT - len, "\n\n%s", buf3 ); } } static void box_markup( char *out, const char *top, const char *sub, ... ) { va_list ap; va_start( ap, sub ); box_vmarkup( out, top, sub, ap ); va_end( ap ); } /* Display buffered errors in an error dialog. */ void box_alert( GtkWidget *par ) { char buf[MAX_DIALOG_TEXT]; if( main_option_batch ) { /* No X, just print. */ fprintf( stderr, "%s\n", error_get_top() ); fprintf( stderr, "%s\n", error_get_sub() ); return; } box_markup( buf, error_get_top(), "%s", error_get_sub() ); box_error( par, "%s", buf ); } /* Make an information dialog. */ void box_vinfo( GtkWidget *par, const char *top, const char *sub, va_list ap ) { char buf[MAX_DIALOG_TEXT]; GtkWidget *idlg; box_vmarkup( buf, top, sub, ap ); idlg = idialog_new(); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) box_build, buf, GTK_STOCK_DIALOG_INFO, NULL ); idialog_set_callbacks( IDIALOG( idlg ), NULL, NULL, NULL, NULL ); idialog_add_ok( IDIALOG( idlg ), iwindow_true_cb, GTK_STOCK_OK ); iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } /* Make an information dialog. */ void box_info( GtkWidget *par, const char *top, const char *sub, ... ) { va_list ap; va_start( ap, sub ); box_vinfo( par, top, sub, ap ); va_end( ap ); } /* Pop up an 'Are you sure?' window. */ iDialog * box_yesno( GtkWidget *par, iWindowFn okcb, iWindowFn cancelcb, void *client, /* Call client */ iWindowNotifyFn nfn, void *sys, /* Call parent */ const char *yes_label, const char *top, const char *sub, ... ) { va_list ap; char buf[MAX_DIALOG_TEXT]; GtkWidget *idlg; va_start( ap, sub ); box_vmarkup( buf, top, sub, ap ); va_end( ap ); idlg = idialog_new(); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) box_build, buf, GTK_STOCK_DIALOG_QUESTION, NULL ); idialog_set_callbacks( IDIALOG( idlg ), cancelcb, NULL, NULL, client ); idialog_add_ok( IDIALOG( idlg ), okcb, "%s", yes_label ); idialog_set_notify( IDIALOG( idlg ), nfn, sys ); iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); return( IDIALOG( idlg ) ); } /* Pop up a `save'/`don't save'/`cancel' dialog. */ void box_savenosave( GtkWidget *par, iWindowFn save, iWindowFn nosave, void *client, /* Call client */ iWindowNotifyFn nfn, void *sys, /* Call parent */ const char *top, const char *sub, ... ) { va_list ap; char buf[MAX_DIALOG_TEXT]; GtkWidget *idlg; va_start( ap, sub ); box_vmarkup( buf, top, sub, ap ); va_end( ap ); idlg = idialog_new(); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) box_build, buf, GTK_STOCK_DIALOG_QUESTION, NULL ); idialog_set_callbacks( IDIALOG( idlg ), iwindow_true_cb, NULL, NULL, client ); idialog_add_ok( IDIALOG( idlg ), nosave, _( "Close _without Saving" ) ); idialog_add_ok( IDIALOG( idlg ), save, GTK_STOCK_SAVE ); idialog_set_notify( IDIALOG( idlg ), nfn, sys ); iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } #define ABOUT(A) ((About *) (A)) /* Make the insides of an about box. */ static void about_build( iDialog *idlg, GtkWidget *work ) { /* Translators: translate this to a credit for you, and it'll appear in * the About box. */ char *translator_credits = _( "translator_credits" ); GtkWidget *hb; GtkWidget *lab; char txt[MAX_DIALOG_TEXT]; char txt2[MAX_DIALOG_TEXT]; VipsBuf buf = VIPS_BUF_STATIC( txt ); GtkWidget *image; im_snprintf( txt2, MAX_DIALOG_TEXT, _( "About %s." ), PACKAGE ); vips_buf_appendf( &buf, "%s\n\n", txt2 ); im_snprintf( txt2, MAX_DIALOG_TEXT, _( "%s is an image processing package." ), PACKAGE ); vips_buf_appendf( &buf, "%s\n\n", txt2 ); im_snprintf( txt2, MAX_DIALOG_TEXT, _( "%s comes with ABSOLUTELY NO WARRANTY. This is " "free software and you are welcome to redistribute " "it under certain conditions, see http://www.gnu.org." ), PACKAGE ); vips_buf_appendf( &buf, "%s\n\n", txt2 ); im_snprintf( txt2, MAX_DIALOG_TEXT, _( NIP_COPYRIGHT ), PACKAGE ); vips_buf_appendf( &buf, "%s\n\n", txt2 ); { char buf1[FILENAME_MAX]; char buf2[FILENAME_MAX]; im_snprintf( buf1, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "start", get_savedir() ); expand_variables( buf1, buf2 ); nativeize_path( buf2 ); escape_markup( buf2, buf1, FILENAME_MAX ); vips_buf_appendf( &buf, "%s: %s\n", _( "Personal start folder" ), buf1 ); } vips_buf_appendf( &buf, "%s: %s\n", _( "Homepage" ), VIPS_HOMEPAGE ); escape_markup( im_version_string(), txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "%s: %s\n", _( "Linked to VIPS" ), txt2 ); escape_markup( IM_VERSION_STRING, txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "%s: %s\n", _( "Built against VIPS" ), txt2 ); escape_markup( PACKAGE, txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "$PACKAGE: %s\n", txt2 ); escape_markup( VERSION, txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "$VERSION: %s\n", txt2 ); escape_markup( NN( g_getenv( "VIPSHOME" ) ), txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "$VIPSHOME: %s\n", txt2 ); escape_markup( NN( g_getenv( "HOME" ) ), txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "$HOME: %s\n", txt2 ); escape_markup( NN( g_getenv( "SAVEDIR" ) ), txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "$SAVEDIR: %s\n", txt2 ); escape_markup( PATH_TMP, txt2, MAX_DIALOG_TEXT ); vips_buf_appendf( &buf, "%s: %s\n", _( "Temp files in" ), txt2 ); if( strcmp( translator_credits, "translator_credits" ) != 0 ) { vips_buf_appendf( &buf, "\n" ); vips_buf_appends( &buf, translator_credits ); } vips_buf_appendf( &buf, "\n" ); mainw_find_disc( &buf ); /* Expands to (eg.) "14GB free in /pics/tmp" */ vips_buf_appendf( &buf, _( " in \"%s\"" ), PATH_TMP ); vips_buf_appends( &buf, "\n" ); vips_buf_appendf( &buf, _( "%d cells in heap, %d cells free, %d cells maximum" ), reduce_context->heap->ncells, reduce_context->heap->nfree, reduce_context->heap->max_fn( reduce_context->heap ) ); vips_buf_appends( &buf, "\n" ); vips_buf_appendf( &buf, _( "%d vips calls cached by nip2" ), cache_history_size ); vips_buf_appends( &buf, "\n" ); vips_buf_appendf( &buf, _( "%d vips operations cached by libvips" ), vips_cache_get_size() ); vips_buf_appends( &buf, "\n" ); vips_buf_appendf( &buf, _( "using %d threads" ), im_concurrency_get() ); vips_buf_appends( &buf, "\n" ); vips_buf_appendf( &buf, _( "%d pixel buffers in vips" ), vips_tracked_get_allocs() ); vips_buf_appends( &buf, "\n" ); vips_buf_append_size( &buf, vips_tracked_get_mem() ); vips_buf_appendf( &buf, _( " of ram in pixel buffers" ) ); vips_buf_appends( &buf, "\n" ); vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() ); vips_buf_appendf( &buf, _( " of ram highwater mark" ) ); vips_buf_appends( &buf, "\n" ); hb = gtk_hbox_new( FALSE, 0 ); gtk_container_border_width( GTK_CONTAINER( hb ), 10 ); gtk_container_add( GTK_CONTAINER( work ), hb ); gtk_widget_show( hb ); image = image_new_from_file( "$VIPSHOME/share/$PACKAGE/data/vips-128.png" ); gtk_box_pack_start( GTK_BOX( hb ), image, FALSE, FALSE, 2 ); gtk_widget_show( image ); lab = gtk_label_new( "" ); gtk_label_set_markup( GTK_LABEL( lab ), vips_buf_all( &buf ) ); gtk_label_set_justify( GTK_LABEL( lab ), GTK_JUSTIFY_LEFT ); gtk_label_set_selectable( GTK_LABEL( lab ), TRUE ); gtk_label_set_line_wrap( GTK_LABEL( lab ), TRUE ); gtk_box_pack_start( GTK_BOX( hb ), lab, FALSE, FALSE, 2 ); gtk_widget_show( lab ); } /* Pop up an "about" window. */ void box_about( GtkWidget *par ) { GtkWidget *idlg; idlg = idialog_new(); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) about_build, NULL, NULL, NULL ); idialog_add_ok( IDIALOG( idlg ), iwindow_true_cb, GTK_STOCK_OK ); iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } /* A big list of all the help tags, plus the file and anchor they are defined * in. See makehelpindex.pl. */ static const char *box_helpindex[][2] = { #include "helpindex.h" }; /* Pop up a help window for a tag. */ void box_help( GtkWidget *par, const char *name ) { int i; for( i = 0; i < IM_NUMBER( box_helpindex ); i++ ) if( strcmp( name, box_helpindex[i][0] ) == 0 ) { char url[512]; im_snprintf( url, 512, "file://%s/%s", NIP_DOCPATH, box_helpindex[i][1] ); box_url( par, url ); return; } error_top( _( "Help page not found." ) ); error_sub( _( "No indexed help page found for tag \"%s\"" ), name ); iwindow_alert( par, GTK_MESSAGE_ERROR ); } /* Name + caption dialog ... for new workspace / new column. */ static iDialogClass *stringset_parent_class = NULL; void * stringset_child_destroy( StringsetChild *ssc ) { ssc->ss->children = g_slist_remove( ssc->ss->children, ssc ); IM_FREE( ssc->label ); IM_FREE( ssc->text ); IM_FREE( ssc->tooltip ); IM_FREE( ssc ); return( NULL ); } StringsetChild * stringset_child_new( Stringset *ss, const char *label, const char *text, const char *tooltip ) { StringsetChild *ssc = INEW( NULL, StringsetChild ); ssc->ss = ss; ssc->label = im_strdup( NULL, label ); ssc->text = im_strdup( NULL, text ); ssc->tooltip = im_strdup( NULL, tooltip ); ss->children = g_slist_append( ss->children, ssc ); return( ssc ); } static void stringset_destroy( GtkObject *object ) { Stringset *ss; g_return_if_fail( object != NULL ); g_return_if_fail( IS_STRINGSET( object ) ); ss = STRINGSET( object ); slist_map( ss->children, (SListMapFn) stringset_child_destroy, NULL ); UNREF( ss->group ); if( GTK_OBJECT_CLASS( stringset_parent_class )->destroy ) GTK_OBJECT_CLASS( stringset_parent_class )->destroy( object ); } static void * stringset_build_set_default( StringsetChild *ssc, iDialog *idlg ) { idialog_set_default_entry( idlg, GTK_ENTRY( ssc->entry ) ); return( NULL ); } static void stringset_build( GtkWidget *widget ) { Stringset *ss = STRINGSET( widget ); iDialog *idlg = IDIALOG( widget ); GSList *p; #ifdef DEBUG printf( "stringset_build: %s\n", IWINDOW( ss )->title ); #endif /*DEBUG*/ /* Call all builds in superclasses. */ if( IWINDOW_CLASS( stringset_parent_class )->build ) IWINDOW_CLASS( stringset_parent_class )->build( widget ); ss->group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL ); for( p = ss->children; p; p = p->next ) { StringsetChild *ssc = (StringsetChild *) p->data; ssc->entry = build_glabeltext4( idlg->work, ss->group, ssc->label ); if( ssc->text ) set_gentry( ssc->entry, "%s", ssc->text ); if( ssc->tooltip ) set_tooltip( ssc->entry, "%s", ssc->tooltip ); } /* Set defaults in reverse, so we get top item with focus. */ (void) slist_map_rev( ss->children, (SListMapFn) stringset_build_set_default, idlg ); gtk_widget_show_all( idlg->work ); } static void stringset_class_init( StringsetClass *class ) { GtkObjectClass *object_class; iWindowClass *iwindow_class; object_class = (GtkObjectClass *) class; iwindow_class = (iWindowClass *) class; object_class->destroy = stringset_destroy; iwindow_class->build = stringset_build; stringset_parent_class = g_type_class_peek_parent( class ); } static void stringset_init( Stringset *ss ) { #ifdef DEBUG printf( "stringset_init: %s\n", IWINDOW( ss )->title ); #endif /*DEBUG*/ ss->children = NULL; } GtkType stringset_get_type( void ) { static GtkType stringset_type = 0; if( !stringset_type ) { static const GtkTypeInfo info = { "Stringset", sizeof( Stringset ), sizeof( StringsetClass ), (GtkClassInitFunc) stringset_class_init, (GtkObjectInitFunc) stringset_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; stringset_type = gtk_type_unique( TYPE_IDIALOG, &info ); } return( stringset_type ); } GtkWidget * stringset_new( void ) { Stringset *ss = gtk_type_new( TYPE_STRINGSET ); return( GTK_WIDGET( ss ) ); } StringsetChild * stringset_child_get( Stringset *ss, const char *label ) { GSList *p; for( p = ss->children; p; p = p->next ) { StringsetChild *ssc = (StringsetChild *) p->data; if( strcmp( label, ssc->label ) == 0 ) return( ssc ); } return( NULL ); } /* Find dialog. */ static iDialogClass *find_parent_class = NULL; static void find_build( GtkWidget *widget ) { Find *find = FIND( widget ); iDialog *idlg = IDIALOG( widget ); #ifdef DEBUG printf( "find_build: %s\n", IWINDOW( find )->title ); #endif /*DEBUG*/ /* Call all builds in superclasses. */ if( IWINDOW_CLASS( find_parent_class )->build ) (*IWINDOW_CLASS( find_parent_class )->build)( widget ); find->search = build_glabeltext4( idlg->work, NULL, _( "Search for" ) ); find->csens = build_gtoggle( idlg->work, _( "Case sensitive" ) ); #ifdef HAVE_GREGEX find->regexp = build_gtoggle( idlg->work, _( "Regular expression" ) ); #endif /*HAVE_GREGEX*/ find->fromtop = build_gtoggle( idlg->work, _( "Search from start" ) ); idialog_set_default_entry( idlg, GTK_ENTRY( find->search ) ); gtk_widget_show_all( idlg->work ); } static void find_class_init( FindClass *class ) { iWindowClass *iwindow_class = (iWindowClass *) class; iwindow_class->build = find_build; find_parent_class = g_type_class_peek_parent( class ); } static void find_init( Find *find ) { #ifdef DEBUG printf( "find_init: %s\n", IWINDOW( find )->title ); #endif /*DEBUG*/ idialog_set_pinup( IDIALOG( find ), TRUE ); } GtkType find_get_type( void ) { static GtkType find_type = 0; if( !find_type ) { static const GtkTypeInfo info = { "Find", sizeof( Find ), sizeof( FindClass ), (GtkClassInitFunc) find_class_init, (GtkObjectInitFunc) find_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; find_type = gtk_type_unique( TYPE_IDIALOG, &info ); } return( find_type ); } GtkWidget * find_new( void ) { Find *find = gtk_type_new( TYPE_FIND ); return( GTK_WIDGET( find ) ); } /* Launch a viewer on a URL. */ void box_url( GtkWidget *par, const char *url ) { #ifdef OS_WIN32 char url2[FILENAME_MAX]; int v; expand_variables( url, url2 ); v = (int) ShellExecute( NULL, "open", url2, NULL, NULL, SW_SHOWNORMAL ); if( v <= 32 ) { error_top( _( "Unable to view help file." ) ); error_sub( _( "Unable to open URL \"%s\", " "windows error code = %d." ), url, v ); iwindow_alert( par, GTK_MESSAGE_ERROR ); } #elif defined OS_DARWIN (void) systemf( "open %s", url ); #elif defined HAVE_XDG_OPEN static gboolean shown = FALSE; if( systemf( "%s %s", XDG_OPEN, url ) ) { error_top( _( "Unable to view help file." ) ); error_sub( _( "Attempt to view URL with xdg-open failed\n%s" ), url ); iwindow_alert( par, GTK_MESSAGE_ERROR ); } else if( !shown ) { error_top( _( "Browser window opened." ) ); error_sub( "%s", _( "You may need to switch desktops to see the " "new window." ) ); iwindow_alert( par, GTK_MESSAGE_INFO ); shown = TRUE; } #else /*default unix-y*/ static gboolean shown = FALSE; char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char txt2[512]; VipsBuf buf2 = VIPS_BUF_STATIC( txt2 ); char url2[FILENAME_MAX]; expand_variables( url, url2 ); vips_buf_appendf( &buf, "%s %s", BOX_BROWSER, BOX_BROWSER_REMOTE ); vips_buf_appendf( &buf2, vips_buf_all( &buf ), url2 ); if( systemf( "%s", vips_buf_all( &buf2 ) ) ) { error_top( _( "Unable to view help file." ) ); error_sub( _( "Attempted to launch browser with command:\n" " %s\n" "You can change this command in Preferences." ), vips_buf_all( &buf2 ) ); iwindow_alert( par, GTK_MESSAGE_ERROR ); } else if( !shown ) { error_top( _( "Browser window opened." ) ); error_sub( "%s", _( "You may need to switch desktops to see the " "new window." ) ); iwindow_alert( par, GTK_MESSAGE_INFO ); shown = TRUE; } #endif /*lots*/ } /* Fontchooser dialog. */ static iDialogClass *fontchooser_parent_class = NULL; static void fontchooser_build( GtkWidget *widget ) { Fontchooser *fontchooser = FONTCHOOSER( widget ); iDialog *idlg = IDIALOG( widget ); #ifdef DEBUG printf( "fontchooser_build: %s\n", IWINDOW( fontchooser )->title ); #endif /*DEBUG*/ /* Call all builds in superclasses. */ if( IWINDOW_CLASS( fontchooser_parent_class )->build ) (*IWINDOW_CLASS( fontchooser_parent_class )->build)( widget ); fontchooser->fontchooser = gtk_font_selection_new(); gtk_box_pack_start( GTK_BOX( idlg->work ), fontchooser->fontchooser, TRUE, TRUE, 2 ); iwindow_set_title( IWINDOW( idlg ), _( "Select Font" ) ); gtk_widget_show_all( idlg->work ); } static void fontchooser_class_init( FontchooserClass *class ) { iWindowClass *iwindow_class; fontchooser_parent_class = g_type_class_peek_parent( class ); iwindow_class = (iWindowClass *) class; iwindow_class->build = fontchooser_build; } static void fontchooser_init( Fontchooser *fontchooser ) { } GtkType fontchooser_get_type( void ) { static GtkType fontchooser_type = 0; if( !fontchooser_type ) { static const GtkTypeInfo info = { "Fontchooser", sizeof( Fontchooser ), sizeof( FontchooserClass ), (GtkClassInitFunc) fontchooser_class_init, (GtkObjectInitFunc) fontchooser_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; fontchooser_type = gtk_type_unique( TYPE_IDIALOG, &info ); } return( fontchooser_type ); } Fontchooser * fontchooser_new( void ) { Fontchooser *fontchooser = gtk_type_new( TYPE_FONTCHOOSER ); return( fontchooser ); } gboolean fontchooser_set_font_name( Fontchooser *fontchooser, const char *font_name ) { if( !gtk_font_selection_set_font_name( GTK_FONT_SELECTION( fontchooser->fontchooser ), font_name ) ) { error_top( _( "Font not found." ) ); error_sub( _( "Font \"%s\" not found on system." ), font_name ); return( FALSE ); } return( TRUE ); } char * fontchooser_get_font_name( Fontchooser *fontchooser ) { return( gtk_font_selection_get_font_name( GTK_FONT_SELECTION( fontchooser->fontchooser ) ) ); } /* Fontbutton. */ /* Our signals. */ enum { SIG_CHANGED, /* New font selected */ SIG_LAST }; static GtkButtonClass *fontbutton_parent_class = NULL; static guint fontbutton_signals[SIG_LAST] = { 0 }; static void fontbutton_finalize( GObject *gobject ) { Fontbutton *fontbutton; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_FONTBUTTON( gobject ) ); fontbutton = FONTBUTTON( gobject ); IM_FREE( fontbutton->font_name ); G_OBJECT_CLASS( fontbutton_parent_class )->finalize( gobject ); } static void fontbutton_ok_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Fontchooser *fontchooser = FONTCHOOSER( iwnd ); Fontbutton *fontbutton = FONTBUTTON( client ); char *font_name; font_name = fontchooser_get_font_name( fontchooser ); fontbutton_set_font_name( fontbutton, font_name ); g_free( font_name ); nfn( sys, IWINDOW_YES ); } static void fontbutton_popdown_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Fontbutton *fontbutton = FONTBUTTON( client ); fontbutton->fontchooser = NULL; nfn( sys, IWINDOW_YES ); } static void fontbutton_clicked( GtkButton *button ) { Fontbutton *fontbutton = FONTBUTTON( button ); if( fontbutton->fontchooser ) gtk_window_present( GTK_WINDOW( fontbutton->fontchooser ) ); else { fontbutton->fontchooser = fontchooser_new(); iwindow_set_title( IWINDOW( fontbutton->fontchooser ), _( "Pick a font" ) ); idialog_set_callbacks( IDIALOG( fontbutton->fontchooser ), iwindow_true_cb, fontbutton_popdown_cb, NULL, fontbutton ); idialog_add_ok( IDIALOG( fontbutton->fontchooser ), fontbutton_ok_cb, _( "Set Font" ) ); iwindow_set_parent( IWINDOW( fontbutton->fontchooser ), GTK_WIDGET( button ) ); idialog_set_pinup( IDIALOG( fontbutton->fontchooser ), TRUE ); iwindow_build( IWINDOW( fontbutton->fontchooser ) ); fontchooser_set_font_name( fontbutton->fontchooser, fontbutton->font_name ); gtk_widget_show( GTK_WIDGET( fontbutton->fontchooser ) ); } } static void fontbutton_real_changed( Fontbutton *fontbutton ) { } static void fontbutton_class_init( FontbuttonClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; GtkButtonClass *bobject_class = (GtkButtonClass *) class; fontbutton_parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = fontbutton_finalize; bobject_class->clicked = fontbutton_clicked; class->changed = fontbutton_real_changed; fontbutton_signals[SIG_CHANGED] = g_signal_new( "changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( FontbuttonClass, changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } static void fontbutton_init( Fontbutton *fontbutton ) { fontbutton->font_name = NULL; fontbutton->fontchooser = NULL; set_tooltip( GTK_WIDGET( fontbutton ), _( "Click to select font" ) ); } GtkType fontbutton_get_type( void ) { static GtkType fontbutton_type = 0; if( !fontbutton_type ) { static const GtkTypeInfo info = { "Fontbutton", sizeof( Fontbutton ), sizeof( FontbuttonClass ), (GtkClassInitFunc) fontbutton_class_init, (GtkObjectInitFunc) fontbutton_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; fontbutton_type = gtk_type_unique( GTK_TYPE_BUTTON, &info ); } return( fontbutton_type ); } Fontbutton * fontbutton_new( void ) { Fontbutton *fontbutton = g_object_new( TYPE_FONTBUTTON, "label", "Sans 12", NULL ); return( fontbutton ); } void fontbutton_set_font_name( Fontbutton *fontbutton, const char *font_name ) { char font[256]; char button_text[256]; int i; if( !fontbutton->font_name || strcmp( fontbutton->font_name, font_name ) != 0 ) { IM_SETSTR( fontbutton->font_name, font_name ); im_strncpy( font, font_name, 256 ); for( i = strlen( font ) - 1; i > 0 && isdigit( font[i] ); i-- ) font[i] = '\0'; im_snprintf( button_text, 256, "%s", font, font_name ); gtk_label_set_markup( GTK_LABEL( gtk_bin_get_child( GTK_BIN( fontbutton ) ) ), button_text ); if( fontbutton->fontchooser ) fontchooser_set_font_name( fontbutton->fontchooser, font_name ); g_signal_emit( G_OBJECT( fontbutton ), fontbutton_signals[SIG_CHANGED], 0 ); } } const char * fontbutton_get_font_name( Fontbutton *fontbutton ) { return( fontbutton->font_name ); } /* Infobar. Optional: it's only in quite recent gtk. */ #ifdef USE_INFOBAR static GtkInfoBarClass *infobar_parent_class = NULL; static void infobar_destroy( GtkObject *object ) { Infobar *infobar; g_return_if_fail( object != NULL ); g_return_if_fail( IS_INFOBAR( object ) ); infobar = INFOBAR( object ); IM_FREEF( g_source_remove, infobar->close_timeout ); IM_FREEF( g_source_remove, infobar->close_animation_timeout ); GTK_OBJECT_CLASS( infobar_parent_class )->destroy( object ); } static void infobar_class_init( InfobarClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; infobar_parent_class = g_type_class_peek_parent( class ); object_class->destroy = infobar_destroy; } static void infobar_init( Infobar *infobar ) { infobar->top = NULL; infobar->sub = NULL; infobar->close_timeout = 0; infobar->close_animation_timeout = 0; infobar->height = 0; } GType infobar_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( InfobarClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) infobar_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Infobar ), 32, /* n_preallocs */ (GInstanceInitFunc) infobar_init, }; type = g_type_register_static( GTK_TYPE_INFO_BAR, "Infobar", &info, 0 ); } return( type ); } static void infobar_cancel_close( Infobar *infobar ) { IM_FREEF( g_source_remove, infobar->close_timeout ); IM_FREEF( g_source_remove, infobar->close_animation_timeout ); gtk_widget_set_size_request( GTK_WIDGET( infobar ), -1, -1 ); } static void infobar_hide( Infobar *infobar ) { infobar_cancel_close( infobar ); gtk_widget_hide( GTK_WIDGET( infobar ) ); gtk_widget_hide( GTK_WIDGET( infobar->sub ) ); gtk_widget_set_sensitive( GTK_WIDGET( infobar->info ), TRUE ); } static gboolean infobar_close_animation_timeout( Infobar *infobar ) { infobar->height -= 20; if( infobar->height <= 0 ) { infobar_hide( infobar ); return( FALSE ); } gtk_widget_set_size_request( GTK_WIDGET( infobar ), -1, infobar->height ); return( TRUE ); } static void infobar_start_close( Infobar *infobar ) { infobar_cancel_close( infobar ); infobar->height = GTK_WIDGET( infobar )->allocation.height; infobar->close_animation_timeout = g_timeout_add( 50, (GSourceFunc) infobar_close_animation_timeout, infobar ); } static gboolean infobar_close_timeout( Infobar *infobar ) { infobar_start_close( infobar ); return( FALSE ); } static void infobar_show( Infobar *infobar ) { infobar_cancel_close( infobar ); infobar->close_timeout = g_timeout_add( 5000, (GSourceFunc) infobar_close_timeout, infobar ); gtk_widget_show( GTK_WIDGET( infobar ) ); } static void infobar_info_cb( GtkWidget *button, Infobar *infobar ) { infobar_cancel_close( infobar ); gtk_widget_show( GTK_WIDGET( infobar->sub ) ); gtk_widget_set_sensitive( GTK_WIDGET( infobar->info ), FALSE ); } static void infobar_close_cb( GtkWidget *button, Infobar *infobar ) { infobar_start_close( infobar ); } Infobar * infobar_new( void ) { Infobar *infobar; GtkWidget *vbox; GtkWidget *content_area; GtkWidget *hbox; GtkWidget *action_area; GtkWidget *button; infobar = g_object_new( TYPE_INFOBAR, NULL ); vbox = gtk_vbox_new( FALSE, 10 ); content_area = gtk_info_bar_get_content_area( GTK_INFO_BAR( infobar ) ); gtk_container_add( GTK_CONTAINER( content_area ), vbox ); gtk_widget_show( vbox ); infobar->top = gtk_label_new( "" ); gtk_label_set_justify( GTK_LABEL( infobar->top ), GTK_JUSTIFY_LEFT ); gtk_label_set_selectable( GTK_LABEL( infobar->top ), TRUE ); gtk_label_set_line_wrap( GTK_LABEL( infobar->top ), TRUE ); gtk_container_add( GTK_CONTAINER( vbox ), infobar->top ); gtk_widget_show( infobar->top ); infobar->sub = gtk_label_new( "" ); gtk_label_set_justify( GTK_LABEL( infobar->sub ), GTK_JUSTIFY_LEFT ); gtk_label_set_selectable( GTK_LABEL( infobar->sub ), TRUE ); gtk_label_set_line_wrap( GTK_LABEL( infobar->sub ), TRUE ); gtk_container_add( GTK_CONTAINER( vbox ), infobar->sub ); /* We can't use gtk_info_bar_add_button(), we need the buttons * horizontally. */ hbox = gtk_hbox_new( FALSE, 2 ); action_area = gtk_info_bar_get_action_area( GTK_INFO_BAR( infobar ) ); gtk_container_add( GTK_CONTAINER( action_area ), hbox ); gtk_widget_show( hbox ); button = gtk_button_new_from_stock( GTK_STOCK_CLOSE ); gtk_box_pack_end( GTK_BOX( hbox ), button, TRUE, TRUE, 2 ); g_signal_connect( button, "clicked", G_CALLBACK( infobar_close_cb ), infobar ); gtk_widget_show( button ); infobar->info = gtk_button_new_from_stock( GTK_STOCK_INFO ); gtk_box_pack_end( GTK_BOX( hbox ), infobar->info, TRUE, TRUE, 2 ); g_signal_connect( infobar->info, "clicked", G_CALLBACK( infobar_info_cb ), infobar ); gtk_widget_show( infobar->info ); return( infobar ); } #else /*!USE_INFOBAR*/ Infobar * infobar_new( void ) { return( NULL ); } #endif /*USE_INFOBAR*/ /* Set the label on an infobar to some marked-up text. */ void infobar_vset( Infobar *infobar, GtkMessageType type, const char *top, const char *sub, va_list ap ) { #ifdef USE_INFOBAR char buf1[MAX_DIALOG_TEXT]; char buf2[MAX_DIALOG_TEXT]; char *p; escape_markup( top, buf1, MAX_DIALOG_TEXT ); im_snprintf( buf2, MAX_DIALOG_TEXT, "%s", buf1 ); gtk_label_set_markup( GTK_LABEL( infobar->top ), buf2 ); (void) im_vsnprintf( buf1, MAX_DIALOG_TEXT, sub, ap ); escape_markup( buf1, buf2, MAX_DIALOG_TEXT ); /* Remove any trailing newlines, they make infobars rather large. */ while( (p = buf2 + strlen( buf2 )) > buf2 && p[-1] == '\n' ) p[-1] = '\0'; gtk_label_set_markup( GTK_LABEL( infobar->sub ), buf2 ); gtk_info_bar_set_message_type( GTK_INFO_BAR( infobar ), type ); infobar_show( infobar ); #endif /*USE_INFOBAR*/ } /* Set the label on an infobar to some marked-up text. */ void infobar_set( Infobar *infobar, GtkMessageType type, const char *top, const char *sub, ... ) { va_list ap; va_start( ap, sub ); infobar_vset( infobar, type, top, sub, ap ); va_end( ap ); } ================================================ FILE: src/boxes.h ================================================ /* decls for boxes. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ void box_alert( GtkWidget *par ); void box_vinfo( GtkWidget *par, const char *top, const char *sub, va_list ap ); void box_info( GtkWidget *par, const char *top, const char *sub, ... ) __attribute__((format(printf, 3, 4))); iDialog *box_yesno( GtkWidget *par, iWindowFn okcb, iWindowFn cancelcb, void *client, /* Call client */ iWindowNotifyFn nfn, void *sys, /* Call parent */ const char *yes_label, const char *top, const char *sub, ... ) __attribute__((format(printf, 9, 10))); void box_savenosave( GtkWidget *par, iWindowFn save, iWindowFn nosave, void *client, /* Call client */ iWindowNotifyFn nfn, void *sys, /* Call parent */ const char *top, const char *sub, ... ) __attribute__((format(printf, 8, 9))); void box_about( GtkWidget *par ); void box_help( GtkWidget *par, const char *name ); /* A dialog showing a bunch of editable strings ... eg. name and caption for * new toolkit etc. etc. */ #define TYPE_STRINGSET (stringset_get_type()) #define STRINGSET( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_STRINGSET, Stringset )) #define STRINGSET_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_STRINGSET, StringsetClass )) #define IS_STRINGSET( obj ) (GTK_CHECK_TYPE( (obj), TYPE_STRINGSET )) #define IS_STRINGSET_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_STRINGSET )) /* A Stringset is a bunch of these. */ typedef struct { struct _Stringset *ss; GtkWidget *entry; char *label; char *text; /* Current text value */ char *tooltip; } StringsetChild; typedef struct _Stringset { iDialog parent; GSList *children; GtkSizeGroup *group; /* Align labels with this */ } Stringset; typedef struct _StringsetClass { iDialogClass parent_class; /* My methods. */ } StringsetClass; void *stringset_child_destroy( StringsetChild *ssc ); StringsetChild *stringset_child_new( Stringset *ss, const char *label, const char *text, const char *tooltip ); GtkType stringset_get_type( void ); GtkWidget *stringset_new( void ); StringsetChild *stringset_child_get( Stringset *, const char *label ); /* Find dialog. */ #define TYPE_FIND (find_get_type()) #define FIND( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_FIND, Find )) #define FIND_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_FIND, FindClass )) #define IS_FIND( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FIND )) #define IS_FIND_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FIND )) typedef struct _Find { iDialog parent; /* My instance vars. */ GtkWidget *search; #ifdef HAVE_GREGEX GtkWidget *regexp; #endif /*HAVE_GREGEX*/ GtkWidget *csens; GtkWidget *fromtop; } Find; typedef struct _FindClass { iDialogClass parent_class; /* My methods. */ } FindClass; GtkType find_get_type( void ); GtkWidget *find_new( void ); void box_url( GtkWidget *par, const char *url ); /* Font chooser window. */ #define TYPE_FONTCHOOSER (fontchooser_get_type()) #define FONTCHOOSER( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_FONTCHOOSER, Fontchooser )) #define FONTCHOOSER_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_FONTCHOOSER, FontchooserClass )) #define IS_FONTCHOOSER( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FONTCHOOSER )) #define IS_FONTCHOOSER_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FONTCHOOSER )) typedef struct _Fontchooser { iDialog parent_object; GtkWidget *fontchooser; /* gtk font select widget */ } Fontchooser; typedef struct _FontchooserClass { iDialogClass parent_class; /* My methods. */ } FontchooserClass; GtkType fontchooser_get_type( void ); Fontchooser *fontchooser_new( void ); gboolean fontchooser_set_font_name( Fontchooser *fontchooser, const char *font_name ); char *fontchooser_get_font_name( Fontchooser * ); /* Font button. */ #define TYPE_FONTBUTTON (fontbutton_get_type()) #define FONTBUTTON( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_FONTBUTTON, Fontbutton )) #define FONTBUTTON_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_FONTBUTTON, FontbuttonClass )) #define IS_FONTBUTTON( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FONTBUTTON )) #define IS_FONTBUTTON_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FONTBUTTON )) typedef struct _Fontbutton { GtkButton parent_object; char *font_name; /* Current name */ Fontchooser *fontchooser; /* Pop up dialog */ } Fontbutton; typedef struct _FontbuttonClass { GtkButtonClass parent_class; void (*changed)( Fontbutton * ); } FontbuttonClass; GtkType fontbutton_get_type( void ); Fontbutton *fontbutton_new( void ); void fontbutton_set_font_name( Fontbutton *fontbutton, const char *font_name ); const char *fontbutton_get_font_name( Fontbutton * ); /* Infobar subclass, with a close animation and a label. */ #define TYPE_INFOBAR (infobar_get_type()) #define INFOBAR( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_INFOBAR, Infobar )) #define INFOBAR_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_INFOBAR, InfobarClass )) #define IS_INFOBAR( obj ) (GTK_CHECK_TYPE( (obj), TYPE_INFOBAR )) #define IS_INFOBAR_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_INFOBAR )) struct _Infobar { #ifdef USE_INFOBAR GtkInfoBar parent_object; #endif /*USE_INFOBAR*/ GtkWidget *top; GtkWidget *sub; GtkWidget *info; guint close_timeout; guint close_animation_timeout; int height; }; typedef struct _InfobarClass { #ifdef USE_INFOBAR GtkInfoBarClass parent_class; #endif /*USE_INFOBAR*/ } InfobarClass; GtkType infobar_get_type( void ); Infobar *infobar_new( void ); void infobar_vset( Infobar *infobar, GtkMessageType type, const char *top, const char *sub, va_list ap ); void infobar_set( Infobar *infobar, GtkMessageType type, const char *top, const char *sub, ... ); ================================================ FILE: src/builtin.c ================================================ /* Run builtin functions ... sin/error etc. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" #ifdef HAVE_GSL #include #include #endif /*HAVE_GSL*/ /* Trace builtin calls. #define DEBUG */ /* Spot something that might be an arg to sin/cos/tan etc. */ static gboolean ismatharg( Reduce *rc, PElement *base ) { return( PEISIMAGE( base ) || PEISREAL( base ) || PEISCOMPLEX( base ) ); } /* Spot something that might be an arg to re/im etc. */ static gboolean iscomplexarg( Reduce *rc, PElement *base ) { return( PEISIMAGE( base ) || PEISCOMPLEX( base ) ); } /* Spot anything. */ static gboolean isany( Reduce *rc, PElement *base ) { return( TRUE ); } /* Other PEIS as functions. */ static gboolean pe_is_image( Reduce *rc, PElement *base ) { return( PEISIMAGE( base ) ); } static gboolean pe_is_real( Reduce *rc, PElement *base ) { return( PEISREAL( base ) ); } static gboolean pe_is_complex( Reduce *rc, PElement *base ) { return( PEISCOMPLEX( base ) ); } static gboolean pe_is_bool( Reduce *rc, PElement *base ) { return( PEISBOOL( base ) ); } static gboolean pe_is_char( Reduce *rc, PElement *base ) { return( PEISCHAR( base ) ); } static gboolean pe_is_list( Reduce *rc, PElement *base ) { return( PEISLIST( base ) ); } static gboolean pe_is_flist( Reduce *rc, PElement *base ) { return( PEISFLIST( base ) ); } static gboolean pe_is_class( Reduce *rc, PElement *base ) { return( PEISCLASS( base ) ); } /* The types we might want to spot for builtins. * * Others, eg.: * static BuiltinTypeSpot vimage_spot = { "vips_image", pe_is_image }; static BuiltinTypeSpot bool_spot = { "bool", pe_is_bool }; static BuiltinTypeSpot realvec_spot = { "[real]", reduce_is_realvec }; static BuiltinTypeSpot matrix_spot = { "[[real]]", reduce_is_matrix }; static BuiltinTypeSpot instance_spot = { "class instance", pe_is_class }; static gboolean pe_is_gobject( Reduce *rc, PElement *base ) { return( PEISMANAGEDGOBJECT( base ) ); } static BuiltinTypeSpot gobject_spot = { "GObject", pe_is_gobject }; * */ static BuiltinTypeSpot real_spot = { "real", pe_is_real }; static BuiltinTypeSpot complex_spot = { "complex|image", iscomplexarg }; static BuiltinTypeSpot flist_spot = { "non-empty list", pe_is_flist }; static BuiltinTypeSpot string_spot = { "[char]", reduce_is_finitestring }; static BuiltinTypeSpot list_spot = { "[*]", reduce_is_list }; static BuiltinTypeSpot math_spot = { "image|real|complex", ismatharg }; static BuiltinTypeSpot any_spot = { "any", isany }; /* Args for "_". */ static BuiltinTypeSpot *underscore_args[] = { &string_spot }; /* Do a _ call. Args already spotted. */ static void apply_underscore_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char text[MAX_STRSIZE]; PEPOINTRIGHT( arg[0], &rhs ); (void) reduce_get_string( rc, &rhs, text, MAX_STRSIZE ); /* Pump though gettext. */ if( !heap_managedstring_new( rc->heap, _( text ), out ) ) reduce_throw( rc ); } /* Args for "has_member". */ static BuiltinTypeSpot *has_member_args[] = { &string_spot, &any_spot }; /* Do a has_member call. Args already spotted. */ static void apply_has_member_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char mname[MAX_STRSIZE]; PElement member; PEPOINTRIGHT( arg[1], &rhs ); (void) reduce_get_string( rc, &rhs, mname, MAX_STRSIZE ); PEPOINTRIGHT( arg[0], &rhs ); PEPUTP( out, ELEMENT_BOOL, class_get_member( &rhs, mname, NULL, &member ) ); } /* Args for "is_instanceof". */ static BuiltinTypeSpot *is_instanceof_args[] = { &string_spot, &any_spot }; /* Do an is_instance call. Args already spotted. */ static void apply_is_instanceof_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char kname[MAX_STRSIZE]; PEPOINTRIGHT( arg[1], &rhs ); (void) reduce_get_string( rc, &rhs, kname, MAX_STRSIZE ); PEPOINTRIGHT( arg[0], &rhs ); PEPUTP( out, ELEMENT_BOOL, reduce_is_instanceof( rc, kname, &rhs ) ); } /* Args for builtin on complex. */ static BuiltinTypeSpot *complex_args[] = { &complex_spot }; /* Do a complex op. Args already spotted. */ static void apply_complex_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; PEPOINTRIGHT( arg[0], &rhs ); if( PEISIMAGE( &rhs ) ) { if( strcmp( name, "re" ) == 0 ) call_spine( rc, "im_c2real", arg, out ); else if( strcmp( name, "im" ) == 0 ) call_spine( rc, "im_c2imag", arg, out ); } else if( PEISCOMPLEX( &rhs ) ) { if( strcmp( name, "re" ) == 0 ) { PEPUTP( out, ELEMENT_NODE, GETLEFT( PEGETVAL( &rhs ) ) ); } else if( strcmp( name, "im" ) == 0 ) { PEPUTP( out, ELEMENT_NODE, GETRIGHT( PEGETVAL( &rhs ) ) ); } } else error( "internal error #98743698437639487" ); } /* Args for builtin on list. */ static BuiltinTypeSpot *flist_args[] = { &flist_spot }; /* Do a list op. Args already spotted. */ static void apply_list_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; PElement a; PEPOINTRIGHT( arg[0], &rhs ); g_assert( PEISFLIST( &rhs ) ); reduce_get_list( rc, &rhs ); if( strcmp( name, "hd" ) == 0 ) { PEGETHD( &a, &rhs ); PEPUTPE( out, &a ); } else if( strcmp( name, "tl" ) == 0 ) { PEGETTL( &a, &rhs ); PEPUTPE( out, &a ); } else error( "internal error #098734953" ); } /* "gammq" */ static BuiltinTypeSpot *gammq_args[] = { &real_spot, &real_spot }; static void apply_gammq_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; double a, x, Q; PEPOINTRIGHT( arg[1], &rhs ); a = PEGETREAL( &rhs ); PEPOINTRIGHT( arg[0], &rhs ); x = PEGETREAL( &rhs ); if( a <= 0 || x < 0 ) { error_top( _( "Out of range." ) ); error_sub( _( "gammq arguments must be a > 0, x >= 0." ) ); reduce_throw( rc ); } #ifdef HAVE_GSL Q = gsl_sf_gamma_inc_Q( a, x ); #else /*!HAVE_GSL*/ error_top( _( "Not available." ) ); error_sub( _( "No GSL library available for gammq." ) ); reduce_throw( rc ); #endif /*HAVE_GSL*/ if( !heap_real_new( rc->heap, Q, out ) ) reduce_throw( rc ); } /* Args for "vips_image". */ static BuiltinTypeSpot *image_args[] = { &string_spot }; /* Do a image call. */ static void apply_image_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { Heap *heap = rc->heap; PElement rhs; char buf[FILENAME_MAX]; char filename[FILENAME_MAX]; char mode[FILENAME_MAX]; char *fn; Imageinfo *ii; /* Get string. */ PEPOINTRIGHT( arg[0], &rhs ); (void) reduce_get_string( rc, &rhs, buf, FILENAME_MAX ); /* The buf might be something like n3862.pyr.tif:1, ie. contain some * load options. Split and search just for the filename component. */ im_filename_split( buf, filename, mode ); /* Try to load image from given string. */ if( !(fn = path_find_file( filename )) ) reduce_throw( rc ); /* Reattach the mode and load. */ im_snprintf( buf, FILENAME_MAX, "%s:%s", fn, mode ); if( !(ii = imageinfo_new_input( main_imageinfogroup, NULL, heap, buf )) ) { IM_FREE( fn ); reduce_throw( rc ); } IM_FREE( fn ); PEPUTP( out, ELEMENT_MANAGED, ii ); MANAGED_UNREF( ii ); } /* Args for "read". */ static BuiltinTypeSpot *read_args[] = { &string_spot }; /* Do a read call. */ static void apply_read_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char buf[FILENAME_MAX]; /* Get string. */ PEPOINTRIGHT( arg[0], &rhs ); (void) reduce_get_string( rc, &rhs, buf, FILENAME_MAX ); if( !heap_file_new( rc->heap, buf, out ) ) reduce_throw( rc ); } /* Args for "graph_export_image". */ static BuiltinTypeSpot *graph_export_image_args[] = { &real_spot, &any_spot }; /* Do a graph_export_image call. */ static void apply_graph_export_image_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { #ifdef HAVE_LIBGOFFICE PElement rhs; double dpi; Plot *plot; Imageinfo *ii; PEPOINTRIGHT( arg[1], &rhs ); dpi = PEGETREAL( &rhs ); PEPOINTRIGHT( arg[0], &rhs ); if( !reduce_is_instanceof( rc, CLASS_PLOT, &rhs ) ) { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); itext_value_ev( rc, &buf, &rhs ); error_top( _( "Bad argument." ) ); error_sub( _( "Argument 2 to \"%s\" should " "be instance of \"%s\", you passed:\n %s" ), name, CLASS_PLOT, vips_buf_all( &buf ) ); reduce_throw( rc ); } plot = g_object_new( TYPE_PLOT, NULL ); if( !classmodel_update_members( CLASSMODEL( plot ), &rhs ) ) { UNREF( plot ); reduce_throw( rc ); } if( !(ii = plot_to_image( plot, rc, dpi )) ) { UNREF( plot ); reduce_throw( rc ); } UNREF( plot ); PEPUTP( out, ELEMENT_MANAGED, ii ); #else /*!HAVE_LIBGOFFICE*/ PEPUTP( out, ELEMENT_BOOL, TRUE ); #endif /*HAVE_LIBGOFFICE*/ } /* Args for "math". */ static BuiltinTypeSpot *math_args[] = { &math_spot }; /* A math function ... name, number implementation, image implementation. */ typedef struct { const char *name; /* ip name */ double (*rfn)( double ); /* Number implementation */ const char *ifn; /* VIPS name */ } MathFn; static double ip_sin( double a ) { return( sin( IM_RAD( a ) ) ); } static double ip_cos( double a ) { return( cos( IM_RAD( a ) ) ); } static double ip_tan( double a ) { return( tan( IM_RAD( a ) ) ); } static double ip_asin( double a ) { return( IM_DEG( asin( a ) ) ); } static double ip_acos( double a ) { return( IM_DEG( acos( a ) ) ); } static double ip_atan( double a ) { return( IM_DEG( atan( a ) ) ); } static double ip_exp10( double a ) { return( pow( 10.0, a ) ); } static double ip_ceil( double a ) { return( ceil( a ) ); } static double ip_floor( double a ) { return( floor( a ) ); } /* Table of math functions ... number implementations, image implementations. */ static MathFn math_fn[] = { { "sin", &ip_sin, "im_sintra" }, { "cos", &ip_cos, "im_costra" }, { "tan", &ip_tan, "im_tantra" }, { "asin", &ip_asin, "im_asintra" }, { "acos", &ip_acos, "im_acostra" }, { "atan", &ip_atan, "im_atantra" }, { "log", &log, "im_logtra" }, { "log10", &log10, "im_log10tra" }, { "exp", &exp, "im_exptra" }, { "exp10", &ip_exp10, "im_exp10tra" }, { "ceil", &ip_ceil, "im_ceil" }, { "floor", &ip_floor, "im_floor" } }; /* Do a math function (eg. sin, cos, tan). */ static void apply_math_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; int i; /* Find implementation. */ for( i = 0; i < IM_NUMBER( math_fn ); i++ ) if( strcmp( name, math_fn[i].name ) == 0 ) break; if( i == IM_NUMBER( math_fn ) ) error( "internal error #928456936" ); /* Get arg type ... real/complex/image */ PEPOINTRIGHT( arg[0], &rhs ); if( PEISIMAGE( &rhs ) ) { /* Easy ... pass to VIPS. */ call_spine( rc, math_fn[i].ifn, arg, out ); } else if( PEISREAL( &rhs ) ) { double a = PEGETREAL( &rhs ); double b = math_fn[i].rfn( a ); if( !heap_real_new( rc->heap, b, out ) ) reduce_throw( rc ); } else if( PEISCOMPLEX( &rhs ) ) { error_top( _( "Not implemented." ) ); error_sub( _( "Complex math ops not implemented." ) ); reduce_throw( rc ); } else error( "internal error #92870653" ); } /* Args for "predicate". */ static BuiltinTypeSpot *pred_args[] = { &any_spot }; /* A predicate function ... name, implementation. */ typedef struct { const char *name; /* ip name */ gboolean (*fn)( Reduce *, PElement * ); /* Implementation */ } PredicateFn; /* Table of predicate functions ... name and implementation. */ static PredicateFn predicate_fn[] = { { "is_image", &pe_is_image }, { "is_bool", &pe_is_bool }, { "is_real", &pe_is_real }, { "is_char", &pe_is_char }, { "is_class", &pe_is_class }, { "is_list", &pe_is_list }, { "is_complex", &pe_is_complex } }; /* Do a predicate function (eg. is_bool) */ static void apply_pred_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; gboolean res; int i; /* Find implementation. */ for( i = 0; i < IM_NUMBER( predicate_fn ); i++ ) if( strcmp( name, predicate_fn[i].name ) == 0 ) break; if( i == IM_NUMBER( predicate_fn ) ) error( "internal error #928456936" ); /* Call! */ PEPOINTRIGHT( arg[0], &rhs ); res = predicate_fn[i].fn( rc, &rhs ); PEPUTP( out, ELEMENT_BOOL, res ); } /* Args for "error". */ static BuiltinTypeSpot *error_args[] = { &string_spot }; /* Do "error". */ static void apply_error_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { char buf[MAX_STRSIZE]; PElement rhs; /* Get string. */ PEPOINTRIGHT( arg[0], &rhs ); (void) reduce_get_string( rc, &rhs, buf, MAX_STRSIZE ); error_top( _( "Macro error." ) ); error_sub( "%s", buf ); reduce_throw( rc ); } /* Args for "search". */ static BuiltinTypeSpot *search_args[] = { &string_spot }; /* Do "search". */ static void apply_search_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { char buf[MAX_STRSIZE]; PElement rhs; char *fn; /* Get string. */ PEPOINTRIGHT( arg[0], &rhs ); (void) reduce_get_string( rc, &rhs, buf, MAX_STRSIZE ); if( !(fn = path_find_file( buf )) ) /* If not found, return []. */ fn = im_strdup( NULL, "" ); if( !heap_managedstring_new( rc->heap, fn, out ) ) { IM_FREE( fn ); reduce_throw( rc ); } IM_FREE( fn ); } /* Args for "print". */ static BuiltinTypeSpot *print_args[] = { &any_spot }; /* Do "print". */ static void apply_print_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); PEPOINTRIGHT( arg[0], &rhs ); itext_value_ev( rc, &buf, &rhs ); if( !heap_managedstring_new( rc->heap, vips_buf_all( &buf ), out ) ) reduce_throw( rc ); } /* Args for "dir". */ static BuiltinTypeSpot *dir_args[] = { &any_spot }; static void * dir_object_member( Symbol *sym, PElement *value, Reduce *rc, PElement *list ) { PElement t; if( !heap_list_add( rc->heap, list, &t ) || !heap_managedstring_new( rc->heap, IOBJECT( sym )->name, &t ) ) reduce_throw( rc ); (void) heap_list_next( list ); return( NULL ); } static void * dir_object( Reduce *rc, PElement *list, PElement *instance, PElement *out ) { PElement p; /* p walks down the list as we build it, list stays pointing at the * head ready to be written to out. */ p = *list; heap_list_init( &p ); class_map( instance, (class_map_fn) dir_object_member, rc, &p ); PEPUTPE( out, list ); return( NULL ); } static void * dir_scope( Symbol *sym, Reduce *rc, PElement *list ) { PElement t; if( !heap_list_add( rc->heap, list, &t ) || !heap_managedstring_new( rc->heap, IOBJECT( sym )->name, &t ) ) reduce_throw( rc ); (void) heap_list_next( list ); return( NULL ); } static void * dir_gtype( GType type, void *a, void *b ) { Reduce *rc = (Reduce *) a; PElement *list = (PElement *) b; PElement t; if( !heap_list_add( rc->heap, list, &t ) || !heap_real_new( rc->heap, type, &t ) ) return( rc ); (void) heap_list_next( list ); return( NULL ); } static void dir_gobject( Reduce *rc, GParamSpec **properties, guint n_properties, PElement *out ) { int i; PElement list; list = *out; heap_list_init( &list ); for( i = 0; i < n_properties; i++ ) { PElement t; if( !heap_list_add( rc->heap, &list, &t ) || !heap_managedstring_new( rc->heap, properties[i]->name, &t ) ) reduce_throw( rc ); (void) heap_list_next( &list ); } } /* Do "dir". */ static void apply_dir_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; PEPOINTRIGHT( arg[0], &rhs ); if( PEISCLASS( &rhs ) ) /* This is more complex than it looks. We have to walk a class * instance generating a list of member names, while not * destroying the instance as we go, in the case that out will * overwrite (rhs) arg[0]. */ reduce_safe_pointer( rc, (reduce_safe_pointer_fn) dir_object, &rhs, out, NULL, NULL ); else if( PEISSYMREF( &rhs ) ) { Symbol *sym = PEGETSYMREF( &rhs ); if( is_scope( sym ) && sym->expr && sym->expr->compile ) { PElement list; list = *out; heap_list_init( &list ); icontainer_map( ICONTAINER( sym->expr->compile ), (icontainer_map_fn) dir_scope, rc, &list ); } } else if( PEISREAL( &rhs ) ) { /* Assume this is a gtype and try to get the children of that * type. */ GType type = PEGETREAL( &rhs ); PElement list; list = *out; heap_list_init( &list ); if( !g_type_name( type ) ) { error_top( _( "No such type" ) ); error_sub( _( "GType %u not found." ), (unsigned int) type ); reduce_throw( rc ); } if( vips_type_map( type, dir_gtype, rc, &list ) ) reduce_throw( rc ); } else if( PEISMANAGEDGOBJECT( &rhs ) ) { guint n_properties; ManagedgobjectClass *class = MANAGEDGOBJECT_GET_CLASS( PEGETMANAGEDGOBJECT( &rhs ) ); GParamSpec **properties; properties = g_object_class_list_properties( G_OBJECT_CLASS( class ), &n_properties ); dir_gobject( rc, properties, n_properties, out ); g_free( properties); } else /* Just [], ie. no names possible. */ heap_list_init( out ); } /* Args for "expand". */ static BuiltinTypeSpot *expand_args[] = { &string_spot }; /* Do "expand". */ static void apply_expand_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char txt[FILENAME_MAX]; char txt2[FILENAME_MAX]; PEPOINTRIGHT( arg[0], &rhs ); (void) reduce_get_string( rc, &rhs, txt, FILENAME_MAX ); expand_variables( txt, txt2 ); if( !heap_managedstring_new( rc->heap, txt2, out ) ) reduce_throw( rc ); } /* Args for "name2gtype". */ static BuiltinTypeSpot *name2gtype_args[] = { &string_spot }; /* Do "name2gtype". */ static void apply_name2gtype_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char txt[FILENAME_MAX]; int gtype; PEPOINTRIGHT( arg[0], &rhs ); (void) reduce_get_string( rc, &rhs, txt, FILENAME_MAX ); gtype = g_type_from_name( txt ); if( !heap_real_new( rc->heap, gtype, out ) ) reduce_throw( rc ); } /* Args for "gtype2name". */ static BuiltinTypeSpot *gtype2name_args[] = { &real_spot }; /* Do "gtype2name". */ static void apply_gtype2name_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; int gtype; PEPOINTRIGHT( arg[0], &rhs ); gtype = PEGETREAL( &rhs ); if( !heap_managedstring_new( rc->heap, g_type_name( gtype ), out ) ) reduce_throw( rc ); } /* Args for "vips_object_new". */ static BuiltinTypeSpot *vo_new_args[] = { &string_spot, &list_spot, &list_spot }; /* Do a vips_object_new call. */ static void apply_vo_new_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char buf[256]; PElement required; PElement optional; PEPOINTRIGHT( arg[2], &rhs ); reduce_get_string( rc, &rhs, buf, 256 ); PEPOINTRIGHT( arg[1], &required ); PEPOINTRIGHT( arg[0], &optional ); vo_object_new( rc, buf, &required, &optional, out ); } /* Args for "vips_call". */ static BuiltinTypeSpot *vo_call_args[] = { &string_spot, &list_spot, &list_spot }; /* Do a vips_call call. */ static void apply_vo_call_call( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { PElement rhs; char buf[256]; PElement required; PElement optional; PEPOINTRIGHT( arg[2], &rhs ); reduce_get_string( rc, &rhs, buf, 256 ); PEPOINTRIGHT( arg[1], &required ); PEPOINTRIGHT( arg[0], &optional ); vo_call( rc, buf, &required, &optional, out ); } /* All ip's builtin functions. */ static BuiltinInfo builtin_table[] = { /* Other. */ { "dir", N_( "return list of names of members" ), FALSE, IM_NUMBER( dir_args ), &dir_args[0], &apply_dir_call }, { "search", N_( "search for file" ), FALSE, IM_NUMBER( search_args ), &search_args[0], &apply_search_call }, { "error", N_( "raise error" ), FALSE, IM_NUMBER( error_args ), &error_args[0], &apply_error_call }, { "print", N_( "convert to [char]" ), FALSE, IM_NUMBER( print_args ), &print_args[0], &apply_print_call }, { "expand", N_( "expand environment variables" ), FALSE, IM_NUMBER( expand_args ), &expand_args[0], &apply_expand_call }, { "name2gtype", N_( "convert [char] to GType" ), FALSE, IM_NUMBER( name2gtype_args ), &name2gtype_args[0], &apply_name2gtype_call }, { "gtype2name", N_( "convert GType to [char]" ), FALSE, IM_NUMBER( gtype2name_args ), >ype2name_args[0], &apply_gtype2name_call }, { "_", N_( "look up localised string" ), FALSE, IM_NUMBER( underscore_args ), &underscore_args[0], &apply_underscore_call }, /* vips8 wrapper. */ { "vips_object_new", N_( "create new vips8 object" ), FALSE, IM_NUMBER( vo_new_args ), &vo_new_args[0], apply_vo_new_call }, { "vips_call", N_( "call vips8 operator" ), FALSE, IM_NUMBER( vo_call_args ), &vo_call_args[0], apply_vo_call_call }, /* Predicates. */ { "is_image", N_( "true if argument is primitive image" ), FALSE, IM_NUMBER( pred_args ), &pred_args[0], apply_pred_call }, { "is_bool", N_( "true if argument is primitive bool" ), FALSE, IM_NUMBER( pred_args ), &pred_args[0], apply_pred_call }, { "is_real", N_( "true if argument is primitive real number" ), FALSE, IM_NUMBER( pred_args ), &pred_args[0], apply_pred_call }, { "is_class", N_( "true if argument is class" ), FALSE, IM_NUMBER( pred_args ), &pred_args[0], apply_pred_call }, { "is_char", N_( "true if argument is primitive char" ), FALSE, IM_NUMBER( pred_args ), &pred_args[0], apply_pred_call }, { "is_list", N_( "true if argument is primitive list" ), FALSE, IM_NUMBER( pred_args ), &pred_args[0], apply_pred_call }, { "is_complex", N_( "true if argument is primitive complex" ), FALSE, IM_NUMBER( pred_args ), &pred_args[0], apply_pred_call }, { "is_instanceof", N_( "true if argument class instance of type" ), FALSE, IM_NUMBER( is_instanceof_args ), &is_instanceof_args[0], apply_is_instanceof_call }, { "has_member", N_( "true if class has named member" ), FALSE, IM_NUMBER( has_member_args ), &has_member_args[0], apply_has_member_call }, /* List and complex projections. */ { "re", N_( "real part of complex" ), TRUE, IM_NUMBER( complex_args ), &complex_args[0], apply_complex_call }, { "im", N_( "imaginary part of complex" ), TRUE, IM_NUMBER( complex_args ), &complex_args[0], apply_complex_call }, { "hd", N_( "head of list" ), TRUE, IM_NUMBER( flist_args ), &flist_args[0], apply_list_call }, { "tl", N_( "tail of list" ), TRUE, IM_NUMBER( flist_args ), &flist_args[0], apply_list_call }, /* Math. */ { "sin", N_( "sine of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "cos", N_( "cosine of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "tan", N_( "tangent of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "asin", N_( "arc sine of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "acos", N_( "arc cosine of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "atan", N_( "arc tangent of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "log", N_( "log base e of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "log10", N_( "log base 10 of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "exp", N_( "e to the power of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "exp10", N_( "10 to the power of real number" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "ceil", N_( "real to int, rounding up" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, { "floor", N_( "real to int, rounding down" ), TRUE, IM_NUMBER( math_args ), &math_args[0], apply_math_call }, /* Optional GSL funcs. */ { "gammq", N_( "gamma function" ), TRUE, IM_NUMBER( gammq_args ), &gammq_args[0], apply_gammq_call }, /* Constructors. */ { "vips_image", N_( "load vips image" ), FALSE, IM_NUMBER( image_args ), &image_args[0], apply_image_call }, { "read", N_( "load text file" ), FALSE, IM_NUMBER( read_args ), &read_args[0], apply_read_call }, { "graph_export_image", N_( "generate image from Plot object" ), FALSE, IM_NUMBER( graph_export_image_args ), &graph_export_image_args[0], apply_graph_export_image_call }, }; #ifdef HAVE_GSL static void builtin_gsl_error( const char *reason, const char *file, int line, int gsl_errno ) { error_top( _( "GSL library error." ) ); error_sub( "%s - (%s:%d) - %s", reason, file, line, gsl_strerror( gsl_errno ) ); reduce_throw( reduce_context ); } #endif /*HAVE_GSL*/ void builtin_init( void ) { Toolkit *kit; int i; /* Make the _builtin toolkit and populate. */ kit = toolkit_new( main_toolkitgroup, "_builtin" ); for( i = 0; i < IM_NUMBER( builtin_table ); i++ ) { Symbol *sym; sym = symbol_new( symbol_root->expr->compile, builtin_table[i].name ); g_assert( sym->type == SYM_ZOMBIE ); sym->type = SYM_BUILTIN; sym->builtin = &builtin_table[i]; (void) tool_new_sym( kit, -1, sym ); symbol_made( sym ); } filemodel_set_auto_load( FILEMODEL( kit ) ); filemodel_set_modified( FILEMODEL( kit ), FALSE ); kit->pseudo = TRUE; /* Start up GSL, if we have it. */ #ifdef HAVE_GSL gsl_set_error_handler( builtin_gsl_error ); #endif /*HAVE_GSL*/ } /* Make a usage error. */ void builtin_usage( VipsBuf *buf, BuiltinInfo *builtin ) { int i; vips_buf_appendf( buf, ngettext( "Builtin \"%s\" takes %d argument.", "Builtin \"%s\" takes %d arguments.", builtin->nargs ), builtin->name, builtin->nargs ); vips_buf_appends( buf, "\n" ); for( i = 0; i < builtin->nargs; i++ ) vips_buf_appendf( buf, " %d - %s\n", i + 1, builtin->args[i]->name ); } #ifdef DEBUG static void builtin_trace_args( Heap *heap, const char *name, int n, HeapNode **arg ) { int i; char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); for( i = 0; i < n; i++ ) { PElement t; PEPOINTRIGHT( arg[n - i - 1], &t ); vips_buf_appends( &buf, "(" ); graph_pelement( heap, &buf, &t, FALSE ); vips_buf_appends( &buf, ") " ); } printf( "builtin: %s %s\n", name, vips_buf_all( &buf ) ); } #endif /*DEBUG*/ /* Execute the internal implementation of a builtin function. */ void builtin_run( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out, BuiltinInfo *builtin ) { int i; /* Typecheck args. */ for( i = 0; i < builtin->nargs; i++ ) { BuiltinTypeSpot *ts = builtin->args[i]; PElement base; PEPOINTRIGHT( arg[builtin->nargs - i - 1], &base ); if( !ts->pred( rc, &base ) ) { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); itext_value_ev( rc, &buf, &base ); error_top( _( "Bad argument." ) ); error_sub( _( "Argument %d to builtin \"%s\" should " "be \"%s\", you passed:\n %s" ), i + 1, name, ts->name, vips_buf_all( &buf ) ); reduce_throw( rc ); } } #ifdef DEBUG builtin_trace_args( rc->heap, name, builtin->nargs, arg ); #endif /*DEBUG*/ builtin->fn( rc, name, arg, out ); } ================================================ FILE: src/builtin.h ================================================ /* Execute builtin 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* A type spotter ... a type name (used in error messages), plus a predicate. */ typedef struct { const char *name; gboolean (*pred)( Reduce *, PElement * ); } BuiltinTypeSpot; /* A builtin function. */ typedef void (*builtin_fn)( Reduce *, const char *, HeapNode **, PElement * ); /* A function name and a pointer to an implementation. */ struct _BuiltinInfo { const char *name; const char *desc; gboolean override; int nargs; BuiltinTypeSpot **args; builtin_fn fn; }; void builtin_init( void ); void builtin_usage( VipsBuf *buf, BuiltinInfo *builtin ); void builtin_run( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out, BuiltinInfo *builtin ); ================================================ FILE: src/cache.c ================================================ /* Call vips functions, cache recent results */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG_TIME #define DEBUG_HISTORY_SANITY #define DEBUG_HISTORY_MISS #define DEBUG_HISTORY #define DEBUG */ /* This is usually turned on from a -D in cflags. #define DEBUG_LEAK */ /* Often want it off ... we get spurious complaints about leaks if an * operation has no images in or out (eg. im_version) because it'll never * get GCed. #undef DEBUG_LEAK */ /* The previous function calls we are caching, plus an LRU queue for flushing. */ static GHashTable *cache_history_table = NULL; static Queue *cache_history_lru = NULL; int cache_history_size = 0; /* Hash from a vargv ... just look at input args and the function name. */ static unsigned int cache_hash( CallInfo *vi ) { int i; unsigned int hash; if( vi->found_hash ) return( vi->hash ); hash = 0; /* add ints, floats, pointers and strings to the hash. FIXME ... could do better on double? could or top and bottom 32 bits but would this be stupid on a 64 bit machine? */ #define HASH_I( I ) hash = (hash << 1) | ((unsigned int) (I)); #define HASH_D( D ) hash = (hash << 1) | ((unsigned int) (D)); #define HASH_P( P ) hash = (hash << 1) | (GPOINTER_TO_UINT( P )); #define HASH_S( S ) hash = (hash << 1) | g_str_hash( S ); /* Add the function to the hash. We often call many functions on * the same args, we'd like these calls to hash to different numbers. */ HASH_P( vi->fn ); for( i = 0; i < vi->fn->argc; i++ ) { im_type_desc *ty = vi->fn->argv[i].desc; if( call_type_needs_input( ty ) ) { switch( call_lookup_type( ty->type ) ) { case CALL_DOUBLE: HASH_D( *((double *) vi->vargv[i]) ); break; case CALL_INT: HASH_I( *((int *) vi->vargv[i]) ); break; case CALL_COMPLEX: HASH_D( ((double *) vi->vargv[i])[0] ); HASH_D( ((double *) vi->vargv[i])[1] ); break; case CALL_STRING: HASH_S( (char *) vi->vargv[i] ); break; case CALL_GVALUE: case CALL_INTERPOLATE: break; case CALL_DOUBLEVEC: { im_doublevec_object *v = (im_doublevec_object *) vi->vargv[i]; int j; for( j = 0; j < v->n; j++ ) HASH_D( v->vec[j] ); break; } case CALL_INTVEC: { im_intvec_object *v = (im_intvec_object *) vi->vargv[i]; int j; for( j = 0; j < v->n; j++ ) HASH_I( v->vec[j] ); break; } case CALL_DMASK: { im_mask_object *mo = vi->vargv[i]; DOUBLEMASK *mask = mo->mask; /* mask can be NULL if we are called after * call_new() but before we've built the arg * list. */ if( mask ) { int ne = mask->xsize * mask->ysize; int j; for( j = 0; j < ne; j++ ) HASH_D( mask->coeff[j] ); HASH_D( mask->scale ); HASH_D( mask->offset ); } break; } case CALL_IMASK: { im_mask_object *mo = vi->vargv[i]; INTMASK *mask = mo->mask; /* mask can be NULL if we are called after * call_new() but before we've built the arg * list. */ if( mask ) { int ne = mask->xsize * mask->ysize; int j; for( j = 0; j < ne; j++ ) HASH_I( mask->coeff[j] ); HASH_I( mask->scale ); HASH_I( mask->offset ); } break; } default: case CALL_NONE: break; } } } /* And the input images. */ for( i = 0; i < vi->ninii; i++ ) HASH_P( vi->inii[i] ); vi->found_hash = TRUE; vi->hash = hash; return( hash ); } /* Are two function calls equal. Check the func and the input args. */ static gboolean cache_equal( CallInfo *vi1, CallInfo *vi2 ) { int i; im_function *fn = vi1->fn; if( vi1 == vi2 ) return( TRUE ); if( vi1->fn != vi2->fn ) return( FALSE ); for( i = 0; i < fn->argc; i++ ) { im_type_desc *ty = fn->argv[i].desc; if( call_type_needs_input( ty ) ) { switch( call_lookup_type( ty->type ) ) { case CALL_DOUBLE: if( *((double *) vi1->vargv[i]) != *((double *) vi2->vargv[i]) ) return( FALSE ); break; case CALL_INT: if( *((int *) vi1->vargv[i]) != *((int *) vi2->vargv[i]) ) return( FALSE ); break; case CALL_COMPLEX: if( ((double *) vi1->vargv[i])[0] != ((double *) vi2->vargv[i])[0] ) return( FALSE ); if( ((double *) vi1->vargv[i])[1] != ((double *) vi2->vargv[i])[1] ) return( FALSE ); break; case CALL_STRING: if( strcmp( (char *) vi1->vargv[i], (char *) vi2->vargv[i] ) != 0 ) return( FALSE ); break; case CALL_DOUBLEVEC: { im_doublevec_object *v1 = (im_doublevec_object *) vi1->vargv[i]; im_doublevec_object *v2 = (im_doublevec_object *) vi2->vargv[i]; int j; for( j = 0; j < v1->n; j++ ) if( v1->vec[j] != v2->vec[j] ) return( FALSE ); break; } case CALL_INTVEC: { im_intvec_object *v1 = (im_intvec_object *) vi1->vargv[i]; im_intvec_object *v2 = (im_intvec_object *) vi2->vargv[i]; int j; for( j = 0; j < v1->n; j++ ) if( v1->vec[j] != v2->vec[j] ) return( FALSE ); break; } case CALL_DMASK: { im_mask_object *mo1 = (im_mask_object *) vi1->vargv[i]; im_mask_object *mo2 = (im_mask_object *) vi2->vargv[i]; DOUBLEMASK *mask1 = mo1->mask; DOUBLEMASK *mask2 = mo2->mask; int ne = mask1->xsize * mask2->ysize; int j; if( mask1->xsize != mask2->xsize || mask1->ysize != mask2->ysize ) return( FALSE ); for( j = 0; j < ne; j++ ) if( mask1->coeff[j] != mask2->coeff[j] ) return( FALSE ); if( mask1->scale != mask2->scale ) return( FALSE ); if( mask1->offset != mask2->offset ) return( FALSE ); break; } case CALL_IMASK: { im_mask_object *mo1 = (im_mask_object *) vi1->vargv[i]; im_mask_object *mo2 = (im_mask_object *) vi2->vargv[i]; INTMASK *mask1 = mo1->mask; INTMASK *mask2 = mo2->mask; int ne = mask1->xsize * mask2->ysize; int j; if( mask1->xsize != mask2->xsize || mask1->ysize != mask2->ysize ) return( FALSE ); for( j = 0; j < ne; j++ ) if( mask1->coeff[j] != mask2->coeff[j] ) return( FALSE ); if( mask1->scale != mask2->scale ) return( FALSE ); if( mask1->offset != mask2->offset ) return( FALSE ); break; } case CALL_IMAGEVEC: { im_imagevec_object *v1 = (im_imagevec_object *) vi1->vargv[i]; im_imagevec_object *v2 = (im_imagevec_object *) vi2->vargv[i]; if( v1->n != v2->n ) return( FALSE ); break; } /* Very strict. Could be more generous here: we'd need * to have a pspec for each argument type and then use * g_param_values_cmp() to test equality. */ case CALL_GVALUE: if( vi1->vargv[i] != vi2->vargv[i] ) return( FALSE ); break; case CALL_INTERPOLATE: if( vi1->vargv[i] != vi2->vargv[i] ) return( FALSE ); break; default: case CALL_NONE: break; } } } /* And the input images. */ if( vi1->ninii != vi2->ninii ) return( FALSE ); for( i = 0; i < vi1->ninii; i++ ) if( vi1->inii[i] != vi2->inii[i] ) return( FALSE ); return( TRUE ); } #ifdef DEBUG_HISTORY_SANITY static void cache_history_sanity_sub( CallInfo *vi ) { g_assert( g_slist_find( cache_history_lru->list, vi ) ); } static void cache_history_sanity( void ) { GSList *p; if( !cache_history_lru || !cache_history_table ) return; /* Everything that's on the LRU should be in the history table. */ for( p = cache_history_lru->list; p; p = p->next ) { CallInfo *vi = (CallInfo *) p->data; g_assert( g_hash_table_lookup( cache_history_table, vi ) ); g_assert( vi->fn ); g_assert( vi->fn->argc > 0 && vi->fn->argc < MAX_CALL_ARGS ); g_assert( vi->in_cache ); } /* Everything that's on the history table should be in the LRU. */ g_hash_table_foreach( cache_history_table, (GHFunc) cache_history_sanity_sub, NULL ); } #endif /*DEBUG_HISTORY_SANITY */ /* Is a function call in our history? Return the old one. */ static CallInfo * cache_history_lookup( CallInfo *vi ) { CallInfo *old_vi; if( !cache_history_table ) { cache_history_table = g_hash_table_new( (GHashFunc) cache_hash, (GEqualFunc) cache_equal ); cache_history_lru = queue_new(); } old_vi = (CallInfo *) g_hash_table_lookup( cache_history_table, vi ); #ifdef DEBUG_HISTORY if( old_vi ) printf( "cache_history_lookup: found \"%s\"\n", old_vi->name ); #endif /*DEBUG_HISTORY*/ #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ return( old_vi ); } /* Bump to end of LRU. */ static void cache_history_touch( CallInfo *vi ) { g_assert( vi->in_cache ); queue_remove( cache_history_lru, vi ); queue_add( cache_history_lru, vi ); #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ #ifdef DEBUG_HISTORY printf( "cache_history_touch: bumping \"%s\"\n", vi->name ); #endif /*DEBUG_HISTORY*/ } /* Are we in the history? Remove us. Called from cache_info_dispose() on unref, * don't call this directly. */ void cache_history_remove( CallInfo *vi ) { int i; if( vi->in_cache ) { queue_remove( cache_history_lru, vi ); g_hash_table_remove( cache_history_table, vi ); cache_history_size -= 1; vi->in_cache = FALSE; #ifdef DEBUG_HISTORY printf( "cache_history_remove: removing \"%s\"\n", vi->name ); #endif /*DEBUG_HISTORY*/ } /* Disconnect signals. */ for( i = 0; i < vi->noutii; i++ ) FREESID( vi->outii_destroy_sid[i], vi->outii[i] ); for( i = 0; i < vi->ninii; i++ ) { FREESID( vi->inii_destroy_sid[i], vi->inii[i] ); FREESID( vi->inii_invalidate_sid[i], vi->inii[i] ); } #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ } static void cache_history_remove_lru( void ) { CallInfo *vi; vi = (CallInfo *) queue_head( cache_history_lru ); #ifdef DEBUG_HISTORY printf( "cache_history_remove_lru: flushing \"%s\"\n", vi->name ); #endif /*DEBUG_HISTORY*/ g_object_unref( vi ); #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ } static void cache_history_destroy_cb( Imageinfo *ii, CallInfo *vi ) { #ifdef DEBUG_HISTORY printf( "cache_history_destroy_cb: on death of ii, uncaching \"%s\"\n", vi->name ); #endif /*DEBUG_HISTORY*/ g_object_unref( vi ); } static void cache_history_invalidate_cb( Imageinfo *ii, CallInfo *vi ) { #ifdef DEBUG_HISTORY printf( "cache_history_invalidate_cb: " "on invalidate of ii, uncaching \"%s\"\n", vi->name ); #endif /*DEBUG_HISTORY*/ g_object_unref( vi ); } /* Add a function call to the history. */ static void cache_history_add( CallInfo *vi ) { int i; #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ #ifdef DEBUG_HISTORY printf( "cache_history_add: adding \"%s\" (%p), hash = %u\n", vi->name, vi, vi->hash ); #endif /*DEBUG_HISTORY*/ g_assert( !g_hash_table_lookup( cache_history_table, vi ) ); g_assert( !vi->in_cache ); g_hash_table_insert( cache_history_table, vi, vi ); cache_history_size += 1; g_assert( g_hash_table_lookup( cache_history_table, vi ) ); queue_add( cache_history_lru, vi ); vi->in_cache = TRUE; g_object_ref( vi ); /* If any of our ii are destroyed, we must go too. */ for( i = 0; i < vi->noutii; i++ ) vi->outii_destroy_sid[i] = g_signal_connect( vi->outii[i], "destroy", G_CALLBACK( cache_history_destroy_cb ), vi ); /* If any of our input ii are destroyed or painted on, we must also * uncache. */ for( i = 0; i < vi->ninii; i++ ) { vi->inii_destroy_sid[i] = g_signal_connect( vi->inii[i], "destroy", G_CALLBACK( cache_history_destroy_cb ), vi ); vi->inii_invalidate_sid[i] = g_signal_connect( vi->inii[i], "invalidate", G_CALLBACK( cache_history_invalidate_cb ), vi ); } /* History too big? Flush! */ if( queue_length( cache_history_lru ) > CALL_HISTORY_MAX ) cache_history_remove_lru(); #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ } /* Sort out the input images. */ static gboolean cache_gather( CallInfo *vi ) { int i, j; int ni; /* No input images. */ if( vi->ninii == 0 ) return( TRUE ); /* Can we LUT? Function needs to be LUTable, all input images * have to be the same underlying image, and image must be uncoded * IM_BANDFMT_UCHAR. */ vi->use_lut = (vi->fn->flags & IM_FN_PTOP) && imageinfo_same_underlying( vi->inii, vi->ninii ) && imageinfo_get_underlying( vi->inii[0] )->Coding == IM_CODING_NONE && imageinfo_get_underlying( vi->inii[0] )->BandFmt == IM_BANDFMT_UCHAR; if( vi->use_lut ) for( i = 0; i < vi->noutii; i++ ) imageinfo_set_underlying( vi->outii[i], vi->inii[0] ); /* Now fill the vargv vector with the IMAGE pointers. */ ni = 0; for( i = 0; i < vi->fn->argc; i++ ) { im_type_desc *ty = vi->fn->argv[i].desc; if( !call_type_needs_input( ty ) ) continue; if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) { Imageinfo *inii = vi->inii[ni++]; IMAGE *im; if( !(im = imageinfo_get( vi->use_lut, inii )) ) return( FALSE ); /* RW operations need an extra copy. Tyhe vargv will * already have been created by cache_build_output(). */ if( ty->flags & IM_TYPE_RW ) { if( im_copy( im, vi->vargv[i] ) ) return( FALSE ); } else vi->vargv[i] = im; } if( strcmp( ty->type, IM_TYPE_IMAGEVEC ) == 0 ) { im_imagevec_object *iv = (im_imagevec_object *) vi->vargv[i]; /* Found an input image vector. Add all the imageinfo * in the vector. */ for( j = 0; j < iv->n; j++ ) { Imageinfo *inii = vi->inii[ni++]; IMAGE *im; if( !(im = imageinfo_get( vi->use_lut, inii )) ) return( FALSE ); iv->vec[j] = im; } } } /* We should have used up all the images exactly. */ g_assert( ni == vi->ninii ); return( TRUE ); } /* VIPS types -> a string buffer. Yuk! Should be a method on object type. This * is used to generate vips history, so it has to be in sh format. */ void cache_tochar_shell( CallInfo *vi, int i, VipsBuf *buf ) { im_object obj = vi->vargv[i]; im_type_desc *ty = vi->fn->argv[i].desc; switch( call_lookup_type( ty->type ) ) { case CALL_DOUBLE: vips_buf_appendf( buf, "%g", *((double*)obj) ); break; case CALL_INT: vips_buf_appendf( buf, "%d", *((int*)obj) ); break; case CALL_COMPLEX: vips_buf_appendf( buf, "(%g, %g)", ((double*)obj)[0], ((double*)obj)[1] ); break; case CALL_STRING: vips_buf_appendf( buf, "\"%s\"", (char*)obj ); break; case CALL_IMAGE: { IMAGE *im = (IMAGE *) obj; /* In quotes, in case there are spaces in the * filename. We also need to test im, as we might be called * before the im has been generated. */ vips_buf_appendf( buf, "\"%s\"", im ? im->filename : "null" ); break; } case CALL_DMASK: case CALL_IMASK: { im_mask_object *mo = obj; vips_buf_appendf( buf, "%s", NN( mo->name ) ); break; } case CALL_DOUBLEVEC: { im_doublevec_object *v = (im_doublevec_object *) obj; int j; vips_buf_appendf( buf, "\"" ); for( j = 0; j < v->n; j++ ) vips_buf_appendf( buf, "%g ", v->vec[j] ); vips_buf_appendf( buf, "\"" ); break; } case CALL_INTVEC: { im_intvec_object *v = (im_intvec_object *) obj; int j; vips_buf_appendf( buf, "\"" ); for( j = 0; j < v->n; j++ ) vips_buf_appendf( buf, "%d ", v->vec[j] ); vips_buf_appendf( buf, "\"" ); break; } case CALL_IMAGEVEC: { im_imagevec_object *v = (im_imagevec_object *) obj; int j; vips_buf_appendf( buf, "\"" ); for( j = 0; j < v->n; j++ ) vips_buf_appendf( buf, "%s ", v->vec[j]->filename ); vips_buf_appendf( buf, "\"" ); break; } case CALL_GVALUE: { GValue *value = (GValue *) obj; vips_buf_appendgv( buf, value ); break; } case CALL_INTERPOLATE: vips_object_to_string( VIPS_OBJECT( obj ), buf ); break; case CALL_NONE: if( strcmp( ty->type, IM_TYPE_DISPLAY ) == 0 ) /* Just assume sRGB. */ vips_buf_appendf( buf, "sRGB" ); break; default: g_assert( FALSE ); } } /* VIPS types -> a buffer. For tracing calls and debug. */ void cache_tochar_trace( CallInfo *vi, int i, VipsBuf *buf ) { im_object obj = vi->vargv[i]; im_type_desc *vips = vi->fn->argv[i].desc; switch( call_lookup_type( vips->type ) ) { case CALL_DOUBLE: vips_buf_appendf( buf, "%g", *((double*)obj) ); break; case CALL_INT: vips_buf_appendf( buf, "%d", *((int*)obj) ); break; case CALL_COMPLEX: vips_buf_appendf( buf, "(%g, %g)", ((double*)obj)[0], ((double*)obj)[1] ); break; case CALL_STRING: vips_buf_appendf( buf, "\"%s\"", (char*) obj ); break; case CALL_IMAGE: vips_buf_appendi( buf, (IMAGE *) obj ); break; case CALL_DMASK: vips_buf_appendf( buf, "dmask" ); break; case CALL_IMASK: vips_buf_appendf( buf, "imask" ); break; case CALL_DOUBLEVEC: vips_buf_appendf( buf, "doublevec" ); break; case CALL_INTVEC: vips_buf_appendf( buf, "intvec" ); break; case CALL_IMAGEVEC: vips_buf_appendf( buf, "imagevec" ); break; case CALL_GVALUE: { GValue *value = (GValue *) obj; vips_buf_appends( buf, "(gvalue" ); vips_buf_appendgv( buf, value ); vips_buf_appendf( buf, ")" ); break; } case CALL_INTERPOLATE: vips_object_to_string( VIPS_OBJECT( obj ), buf ); break; default: g_assert( FALSE ); } } /* Get the args from the VIPS call buffer. */ static void cache_args_vips( CallInfo *vi, VipsBuf *buf ) { int i; vips_buf_appendf( buf, _( "You passed:" ) ); vips_buf_appendf( buf, "\n" ); for( i = 0; i < vi->fn->argc; i++ ) { im_type_desc *ty = vi->fn->argv[i].desc; char *name = vi->fn->argv[i].name; if( call_type_needs_input( ty ) ) { vips_buf_appendf( buf, " %s - ", name ); cache_tochar_trace( vi, i, buf ); vips_buf_appendf( buf, "\n" ); } } } /* There's a problem calling the function. Show args from the vips call * struct. */ static void cache_error_fn_vips( CallInfo *vi ) { char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); error_top( _( "VIPS library error." ) ); vips_buf_appendf( &buf, _( "Error calling library function \"%s\" (%s)." ), vi->name, vi->fn->desc ); vips_buf_appendf( &buf, "\n" ); vips_buf_appendf( &buf, _( "VIPS library: %s" ), im_error_buffer() ); im_error_clear(); vips_buf_appendf( &buf, "\n" ); cache_args_vips( vi, &buf ); vips_buf_appendf( &buf, "\n" ); call_usage( &buf, vi->fn ); error_sub( "%s", vips_buf_all( &buf ) ); } static gboolean cache_build_argv( CallInfo *vi, char **argv ) { int i; for( i = 0; i < vi->fn->argc; i++ ) { char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); cache_tochar_shell( vi, i, &buf ); if( !(argv[i] = im_strdup( NULL, vips_buf_all( &buf ) )) ) return( FALSE ); } #ifdef DEBUG printf( "cache_build_argv: argv for %s is:\n ", vi->fn->name ); for( i = 0; i < vi->fn->argc; i++ ) printf( "%s ", NN( argv[i] ) ); printf( "\n" ); #endif /*DEBUG*/ return( TRUE ); } static void cache_free_argv( int argc, char **argv ) { int i; for( i = 0; i < argc; i++ ) { IM_FREE( argv[i] ); } IM_FREE( argv ); } /* Update the VIPS hist for all output images. */ static void cache_update_hist( CallInfo *vi ) { int argc = vi->fn->argc; char **argv; int i; #ifdef DEBUG printf( "cache_update_hist: %s\n", vi->name ); #endif /*DEBUG*/ /* No output images? Nothing to do. */ if( vi->nires == 0 ) return; /* Build an argv for this call. +1 for NULL termination. */ if( !(argv = IM_ARRAY( NULL, argc + 1, char * )) ) return; for( i = 0; i < argc + 1; i++ ) argv[i] = NULL; if( !cache_build_argv( vi, argv ) ) { cache_free_argv( argc, argv ); return; } for( i = 0; i < vi->nres; i++ ) { int j = vi->outpos[i]; im_type_desc *ty = vi->fn->argv[j].desc; /* Image output. */ if( call_lookup_type( ty->type ) == CALL_IMAGE ) { #ifdef DEBUG printf( "cache_update_hist: adding to arg %d\n", j ); #endif /*DEBUG*/ im_updatehist( vi->vargv[j], vi->fn->name, argc, argv ); } } cache_free_argv( argc, argv ); } /* Call a vips operation. * * The cache takes ownership of the CallInfo passed in, and returns a ref to a * CallInfo (might be a different one) that contains the result. Should be * unreffed when you're done with it. * * On error, return NULL. */ CallInfo * cache_dispatch( CallInfo *vi, PElement *out ) { CallInfo *old_vi; #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ /* Calculate the hash for this vi after building it, but before we do * cache_gather(); * * We want the hash to reflect the args as supplied by nip2, not the * args as transformed by cache_gather() for this specific call. */ (void) cache_hash( vi ); /* Look over the images we have and turn input Imageinfos to IMAGEs. * If we can do this with a lut, set all that up. */ if( !cache_gather( vi ) ) { g_object_unref( vi ); return( NULL ); } /* We have to show args after gather, since the tracer wants IMAGE not * Imageinfo. */ #ifdef DEBUG { int i; for( i = 0; i < vi->fn->argc; i++ ) { im_type_desc *ty = vi->fn->argv[i].desc; char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "cache_fill_spine: arg[%d] (%s) = ", i, ty->type ); cache_tochar_trace( vi, i, &buf ); printf( "%s\n", vips_buf_all( &buf ) ); } } #endif /*DEBUG*/ /* Is this function call in the history? */ if( (old_vi = cache_history_lookup( vi )) ) { /* Yes: reuse! unref our arg to junk it, adda ref to the * cached call for our caller. */ g_object_unref( vi ); vi = old_vi; g_object_ref( vi ); if( trace_flags & TRACE_VIPS ) vips_buf_appendf( trace_current(), "(from cache) " ); #ifdef DEBUG_HISTORY printf( "cache_dispatch: found %s in history\n", vi->name ); #endif /*DEBUG_HISTORY*/ } else { /* No: call function. */ int result; #ifdef DEBUG_TIME static GTimer *timer = NULL; if( !timer ) timer = g_timer_new(); g_timer_reset( timer ); #endif /*DEBUG_TIME*/ #ifdef DEBUG_HISTORY_MISS printf( "cache_dispatch: calling %s\n", vi->name ); #endif /*DEBUG_HISTORY_MISS*/ /* Be careful. Eval callbacks from this may do anything, * including call cache_dispatch(). */ result = vi->fn->disp( vi->vargv ); #ifdef DEBUG_TIME printf( "cache_dispatch: %s - %g seconds\n", vi->name, g_timer_elapsed( timer, NULL ) ); #endif /*DEBUG_TIME*/ if( result ) { cache_error_fn_vips( vi ); g_object_unref( vi ); return( NULL ); } cache_update_hist( vi ); } /* Add to our operation cache, if necessary. */ if( !(vi->fn->flags & IM_FN_NOCACHE) ) { if( vi->in_cache ) /* Already in the history. Just touch the time. */ cache_history_touch( vi ); else if( (old_vi = cache_history_lookup( vi )) ) { /* We have an equal but older item there? This can * happen with nested calls. Touch the old one. */ cache_history_touch( old_vi ); vi = old_vi; } else cache_history_add( vi ); } #ifdef DEBUG_HISTORY_SANITY cache_history_sanity(); #endif /*DEBUG_HISTORY_SANITY*/ return( vi ); } ================================================ FILE: src/cache.h ================================================ /* Call vips functions from the graph reducer. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ extern int cache_history_size; void cache_tochar_trace( CallInfo *vi, int i, VipsBuf *buf ); void cache_history_remove( CallInfo *vi ); CallInfo *cache_dispatch( CallInfo *vi, PElement *out ); ================================================ FILE: src/call.c ================================================ /* Call vips functions from the graph reducer. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with CALL - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG_TIME #define DEBUG */ /* This is usually turned on from a -D in cflags. #define DEBUG_LEAK */ /* Often want it off ... we get spurious complaints about leaks if an * operation has no images in or out (eg. im_version) because it'll never * get GCed. #undef DEBUG_LEAK */ /* CALL argument types we support. Keep order in sync with CallArgumentType. */ static im_arg_type call_supported[] = { IM_TYPE_DOUBLE, IM_TYPE_INT, IM_TYPE_COMPLEX, IM_TYPE_STRING, IM_TYPE_IMAGE, IM_TYPE_DOUBLEVEC, IM_TYPE_DMASK, IM_TYPE_IMASK, IM_TYPE_IMAGEVEC, IM_TYPE_INTVEC, IM_TYPE_GVALUE, IM_TYPE_INTERPOLATE }; static iObjectClass *parent_class = NULL; /* All the CallInfo we make ... for leak and sanity testing. Build this file * with DEBUG_LEAK to enable add/remove to this list. */ GSList *call_info_all = NULL; void call_check_all_destroyed( void ) { #ifdef DEBUG_LEAK int n_leaks; GSList *p; n_leaks = 0; for( p = call_info_all; p; p = p->next ) { CallInfo *vi = (CallInfo *) p->data; /* Operations which don't take an image as either an input or * output will stay in the cache. Don't report them. */ if( vi->ninii || vi->noutii ) { n_leaks += 1; printf( "\t%s\n", vi->name ); } } if( n_leaks ) printf( "** %d CallInfo leaked!\n", n_leaks ); #endif /*DEBUG_LEAK*/ } /* Does a vips argument type require an argument from nip2? */ gboolean call_type_needs_input( im_type_desc *ty ) { /* We supply these. */ if( strcmp( ty->type, IM_TYPE_DISPLAY ) == 0 ) return( FALSE ); if( !(ty->flags & IM_TYPE_OUTPUT) ) return( TRUE ); if( ty->flags & IM_TYPE_RW ) return( TRUE ); return( FALSE ); } /* Will a vips argument type generate a result for nip2? */ gboolean call_type_makes_output( im_type_desc *ty ) { /* We ignore these. */ if( strcmp( ty->type, IM_TYPE_DISPLAY ) == 0 ) return( FALSE ); if( ty->flags & (IM_TYPE_OUTPUT | IM_TYPE_RW) ) return( TRUE ); return( FALSE ); } /* Error early on .. we can't print args yet. */ void call_error( CallInfo *vi ) { error_top( _( "CALL library error." ) ); error_sub( _( "Error calling library function \"%s\" (%s)." ), vi->name, vi->fn->desc ); } /* Get the args from the heap. */ static void call_args_heap( CallInfo *vi, HeapNode **arg, VipsBuf *buf ) { int i; vips_buf_appendf( buf, _( "You passed:" ) ); vips_buf_appendf( buf, "\n" ); for( i = 0; i < vi->nargs; i++ ) { im_arg_desc *varg = &vi->fn->argv[vi->inpos[i]]; PElement rhs; PEPOINTRIGHT( arg[vi->nargs - i - 1], &rhs ); vips_buf_appendf( buf, " %s - ", varg->name ); itext_value_ev( vi->rc, buf, &rhs ); vips_buf_appendf( buf, "\n" ); } } /* Make a usage error for a CALL function. */ void call_usage( VipsBuf *buf, im_function *fn ) { im_package *pack = im_package_of_function( fn->name ); char input[MAX_STRSIZE]; char output[MAX_STRSIZE]; int nout, nin; int i; strcpy( input, "" ); strcpy( output, "" ); nin = 0; nout = 0; for( i = 0; i < fn->argc; i++ ) { im_arg_desc *arg = &fn->argv[i]; char line[256]; /* Format name, type message. */ im_snprintf( line, 256, " %s - %s\n", arg->name, arg->desc->type ); if( call_type_makes_output( arg->desc ) ) { strcat( output, line ); nout++; } if( call_type_needs_input( arg->desc ) ) { strcat( input, line ); nin++; } } vips_buf_appendf( buf, _( "Usage:" ) ); vips_buf_appends( buf, "\n" ); vips_buf_appendf( buf, _( "CALL operator \"%s\"" ), fn->name ); vips_buf_appends( buf, "\n" ); vips_buf_appendf( buf, _( "%s, from package \"%s\"" ), fn->desc, pack->name ); vips_buf_appends( buf, "\n" ); vips_buf_appendf( buf, ngettext( "\"%s\" takes %d argument:", "\"%s\" takes %d arguments:", nin ), fn->name, nin ); vips_buf_appendf( buf, "\n%s", input ); vips_buf_appendf( buf, ngettext( "And produces %d result:", "And produces %d results:", nout ), nout ); vips_buf_appendf( buf, "\n%s", output ); /* Print any flags this function has. */ vips_buf_appendf( buf, _( "Flags:" ) ); vips_buf_appends( buf, "\n" ); vips_buf_appendf( buf, " (" ); if( fn->flags & IM_FN_PIO ) vips_buf_appendf( buf, _( "PIO function" ) ); else vips_buf_appendf( buf, _( "WIO function" ) ); vips_buf_appendf( buf, ") (" ); if( fn->flags & IM_FN_TRANSFORM ) vips_buf_appendf( buf, _( "coordinate transformer" ) ); else vips_buf_appendf( buf, _( "no coordinate transformation" ) ); vips_buf_appendf( buf, ") (" ); if( fn->flags & IM_FN_PTOP ) vips_buf_appendf( buf, _( "point-to-point operation" ) ); else vips_buf_appendf( buf, _( "area operation" ) ); vips_buf_appendf( buf, ") (" ); if( fn->flags & IM_FN_NOCACHE ) vips_buf_appendf( buf, _( "uncacheable operation" ) ); else vips_buf_appendf( buf, _( "operation can be cached" ) ); vips_buf_appendf( buf, ")\n" ); } /* We know there's a problem exporting a particular arg to CALL. */ static void call_error_arg( CallInfo *vi, HeapNode **arg, int argi ) { char txt[10000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); error_top( _( "Bad argument." ) ); vips_buf_appendf( &buf, _( "Argument %d (%s) to \"%s\" is the wrong type." ), argi + 1, vi->fn->argv[vi->inpos[argi]].name, vi->name ); vips_buf_appendf( &buf, "\n" ); call_args_heap( vi, arg, &buf ); vips_buf_appendf( &buf, "\n" ); call_usage( &buf, vi->fn ); error_sub( "%s", vips_buf_all( &buf ) ); } /* Too many args. */ void call_error_toomany( CallInfo *vi ) { char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); error_top( _( "Too many arguments." ) ); vips_buf_appendf( &buf, _( "Too many arguments to \"%s\"." ), vi->name ); vips_buf_appendf( &buf, "\n" ); call_usage( &buf, vi->fn ); error_sub( "%s", vips_buf_all( &buf ) ); } /* Look up a CALL type. */ CallArgumentType call_lookup_type( im_arg_type type ) { int i; for( i = 0; i < IM_NUMBER( call_supported ); i++ ) if( strcmp( type, call_supported[i] ) == 0 ) return( (CallArgumentType) i ); error_top( _( "Unknown type." ) ); error_sub( _( "CALL type \"%s\" not supported" ), type ); return( CALL_NONE ); } /* Is this the sort of CALL function we can call? */ gboolean call_is_callable( im_function *fn ) { int i; int nout; int nin; if( fn->argc >= MAX_CALL_ARGS ) return( FALSE ); /* Check all argument types are supported. As well as the arg types * spotted by call_lookup_type, we also allow IM_TYPE_DISPLAY. */ for( i = 0; i < fn->argc; i++ ) { im_arg_desc *arg = &fn->argv[i]; im_arg_type vt = arg->desc->type; if( call_lookup_type( vt ) == CALL_NONE ) { /* Unknown type .. if DISPLAY it's OK. */ if( strcmp( vt, IM_TYPE_DISPLAY ) != 0 ) return( FALSE ); } } nin = nout = 0; for( i = 0; i < fn->argc; i++ ) { im_type_desc *ty = fn->argv[i].desc; if( call_type_makes_output( ty ) ) nout += 1; if( call_type_needs_input( ty ) ) nin += 1; } /* Must be at least one output argument. */ /* Must be at least one output argument. */ if( nout == 0 ) return( FALSE ); /* Need at least 1 input argument: we reply on having an application * node to overwrite with (I result). */ if( nin == 0 ) return( FALSE ); return( TRUE ); } /* Count the number of args a CALL function needs. */ int call_n_args( im_function *fn ) { int i; int nin; for( nin = 0, i = 0; i < fn->argc; i++ ) { im_type_desc *ty = fn->argv[i].desc; if( call_type_needs_input( ty ) ) nin += 1; } return( nin ); } /* Make an im_doublevec_object. */ static int call_make_doublevec( im_doublevec_object *dv, int n, double *vec ) { int i; dv->n = n; dv->vec = NULL; if( n > 0 ) { if( !(dv->vec = IARRAY( NULL, n, double )) ) return( -1 ); for( i = 0; i < n; i++ ) dv->vec[i] = vec[i]; } return( 0 ); } /* Make an im_intvec_object. Make from a vec of doubles, because that's what * we get from nip. */ static int call_make_intvec( im_intvec_object *dv, int n, double *vec ) { int i; dv->n = n; dv->vec = NULL; if( n > 0 ) { if( !(dv->vec = IARRAY( NULL, n, int )) ) return( -1 ); for( i = 0; i < n; i++ ) dv->vec[i] = vec[i]; } return( 0 ); } /* Make an im_imagevec_object. */ static int call_make_imagevec( im_imagevec_object *iv, int n ) { int i; iv->n = n; iv->vec = NULL; if( n > 0 ) { if( !(iv->vec = IARRAY( NULL, n, IMAGE * )) ) return( -1 ); for( i = 0; i < n; i++ ) iv->vec[i] = NULL; } return( 0 ); } /* Add another ii to inii. */ static gboolean call_add_input_ii( CallInfo *vi, Imageinfo *ii ) { if( vi->ninii > MAX_CALL_ARGS ) { call_error_toomany( vi ); return( FALSE ); } vi->inii[vi->ninii] = ii; vi->ninii += 1; /* We hold a ref to the ii until the call is done and the result * written back to nip2. If we cache the result, we make a new * weakref. */ managed_dup_nonheap( MANAGED( ii ) ); vi->must_drop = TRUE; return( TRUE ); } /* ip types -> CALL types. Write to obj. FALSE for no conversion possible. */ static gboolean call_fromip( CallInfo *vi, int i, PElement *arg ) { im_type_desc *ty = vi->fn->argv[i].desc; CallArgumentType vt = call_lookup_type( ty->type ); im_object *obj = &vi->vargv[i]; /* If call_lookup_type failed, is it the special DISPLAY type? */ if( vt == CALL_NONE && strcmp( ty->type, IM_TYPE_DISPLAY ) != 0 ) /* Unknown type, and it's not DISPLAY. Flag an error. */ return( FALSE ); switch( vt ) { case CALL_NONE: /* IM_TYPE_DISPLAY */ /* Just use IM_TYPE_sRGB. */ *obj = im_col_displays( 7 ); break; case CALL_DOUBLE: { double *a = *obj; if( !PEISREAL( arg ) ) return( FALSE ); *a = PEGETREAL( arg ); break; } case CALL_INT: { int *i = *obj; if( PEISREAL( arg ) ) { double t = PEGETREAL( arg ); *i = (int) t; } else if( PEISBOOL( arg ) ) *i = PEGETBOOL( arg ); else return( FALSE ); break; } case CALL_COMPLEX: { double *c = *obj; if( !PEISCOMPLEX( arg ) ) return( FALSE ); c[0] = PEGETREALPART( arg ); c[1] = PEGETIMAGPART( arg ); break; } case CALL_STRING: { char **c = (char **) obj; char buf[MAX_STRSIZE]; if( !heap_get_string( arg, buf, MAX_STRSIZE ) ) return( FALSE ); *c = im_strdup( NULL, buf ); break; } case CALL_IMAGE: /* Just note the Imageinfo for now ... a later pass sets vargv * once we've checked all the LUTs. */ if( !PEISIMAGE( arg ) || !call_add_input_ii( vi, IMAGEINFO( PEGETII( arg ) ) ) ) return( FALSE ); break; case CALL_DOUBLEVEC: { double buf[MAX_VEC]; int n; if( (n = heap_get_realvec( arg, buf, MAX_VEC )) < 0 || call_make_doublevec( *obj, n, buf ) ) return( FALSE ); break; } case CALL_INTVEC: { double buf[MAX_VEC]; int n; if( (n = heap_get_realvec( arg, buf, MAX_VEC )) < 0 || call_make_intvec( *obj, n, buf ) ) return( FALSE ); break; } case CALL_IMAGEVEC: { Imageinfo *buf[MAX_VEC]; int n; int i; /* Put Imageinfo in for now ... a later pass changes this to * IMAGE* once we've checked all the LUTs. */ if( (n = heap_get_imagevec( arg, buf, MAX_VEC )) < 0 || call_make_imagevec( *obj, n ) ) return( FALSE ); for( i = 0; i < n; i++ ) if( !call_add_input_ii( vi, buf[i] ) ) return( FALSE ); break; } case CALL_DMASK: case CALL_IMASK: { im_mask_object **mo = (im_mask_object **) obj; if( vt == 6 ) { DOUBLEMASK *mask; if( !(mask = matrix_ip_to_dmask( arg )) ) return( FALSE ); (*mo)->mask = mask; (*mo)->name = im_strdupn( mask->filename ); } else { INTMASK *mask; if( !(mask = matrix_ip_to_imask( arg )) ) return( FALSE ); (*mo)->mask = mask; (*mo)->name = im_strdupn( mask->filename ); } break; } case CALL_GVALUE: { GValue *value = *obj; memset( value, 0, sizeof( GValue ) ); if( !heap_ip_to_gvalue( arg, value ) ) return( FALSE ); break; } case CALL_INTERPOLATE: if( !PEISMANAGEDGOBJECT( arg ) ) return( FALSE ); *obj = PEGETMANAGEDGOBJECT( arg ); break; default: g_assert( FALSE ); } return( TRUE ); } /* CALL types -> ip types. Write to arg. Use outiiindex to iterate through * outii[] as we find output imageinfo. */ static gboolean call_toip( CallInfo *vi, int i, int *outiiindex, PElement *arg ) { im_object obj = vi->vargv[i]; im_type_desc *ty = vi->fn->argv[i].desc; #ifdef DEBUG printf( "call_toip: arg[%d] (%s) = ", i, ty->type ); #endif /*DEBUG*/ switch( call_lookup_type( ty->type ) ) { case CALL_DOUBLE: if( !heap_real_new( vi->rc->heap, *((double*)obj), arg ) ) return( FALSE ); break; case CALL_INT: if( !heap_real_new( vi->rc->heap, *((int*)obj), arg ) ) return( FALSE ); break; case CALL_DOUBLEVEC: { im_doublevec_object *dv = obj; if( !heap_realvec_new( vi->rc->heap, dv->n, dv->vec, arg ) ) return( FALSE ); break; } case CALL_INTVEC: { im_intvec_object *iv = obj; if( !heap_intvec_new( vi->rc->heap, iv->n, iv->vec, arg ) ) return( FALSE ); break; } case CALL_COMPLEX: if( !heap_complex_new( vi->rc->heap, ((double*)obj)[0], ((double*)obj)[1], arg ) ) return( FALSE ); break; case CALL_STRING: if( !heap_managedstring_new( vi->rc->heap, (char *) obj, arg ) ) return( FALSE ); break; case CALL_IMAGE: { Imageinfo *outii; outii = vi->outii[*outiiindex]; *outiiindex += 1; PEPUTP( arg, ELEMENT_MANAGED, outii ); break; } case CALL_DMASK: { im_mask_object *mo = obj; DOUBLEMASK *mask = mo->mask; if( !matrix_dmask_to_heap( vi->rc->heap, mask, arg ) ) return( FALSE ); break; } case CALL_IMASK: { im_mask_object *mo = obj; INTMASK *mask = mo->mask; if( !matrix_imask_to_heap( vi->rc->heap, mask, arg ) ) return( FALSE ); break; } case CALL_GVALUE: if( !heap_gvalue_to_ip( (GValue *) obj, arg ) ) return( FALSE ); break; case CALL_IMAGEVEC: case CALL_INTERPOLATE: default: g_assert( FALSE ); } #ifdef DEBUG pgraph( arg ); #endif /*DEBUG*/ return( TRUE ); } static void * call_write_result_sub( Reduce *rc, PElement *safe, CallInfo *vi, PElement *out ) { int outiiindex; /* call_toip() uses this to iterate through outii[]. */ outiiindex = 0; /* Write result. */ if( vi->nres == 1 ) { /* Single result. */ if( !call_toip( vi, vi->outpos[0], &outiiindex, safe ) ) return( out ); } else { /* Have to build a list of results. */ PElement list; PElement t; int i; list = *safe; heap_list_init( &list ); for( i = 0; i < vi->nres; i++ ) { if( !heap_list_add( vi->rc->heap, &list, &t ) || !call_toip( vi, vi->outpos[i], &outiiindex, &t ) ) return( out ); (void) heap_list_next( &list ); } } /* Now overwrite out with safe. */ PEPUTPE( out, safe ); return( NULL ); } /* Write the results back to the heap. We have to so this in two stages: * build the output object linked off a new managed Element, then once it's * built, overwrite our output */ static gboolean call_write_result( CallInfo *vi, PElement *out ) { if( reduce_safe_pointer( vi->rc, (reduce_safe_pointer_fn) call_write_result_sub, vi, out, NULL, NULL ) ) return( FALSE ); return( TRUE ); } /* Junk all the refs we were holding during the call. See call_add_input_ii() * and call_add_output_ii(). * * This gets called explicitly after we have handed the ii refs back to nip2 * during normal processing, or from _dispose() if we bomb out early and * unref. */ static void call_drop_refs( CallInfo *vi ) { if( vi->must_drop ) { int i; #ifdef DEBUG printf( "call_drop_refs: dropping %d in refs\n", vi->ninii ); printf( "call_drop_refs: dropping %d out refs\n", vi->noutii ); #endif /*DEBUG*/ for( i = 0; i < vi->ninii; i++ ) managed_destroy_nonheap( MANAGED( vi->inii[i] ) ); for( i = 0; i < vi->noutii; i++ ) managed_destroy_nonheap( MANAGED( vi->outii[i] ) ); vi->must_drop = FALSE; } } static void call_info_dispose( GObject *gobject ) { CallInfo *vi; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_CALL_INFO( gobject ) ); vi = CALL_INFO( gobject ); #ifdef DEBUG printf( "call_info_dispose: (%p) %s \"%s\"\n", vi, G_OBJECT_TYPE_NAME( vi ), vi->name ); #endif /*DEBUG*/ /* Are we in the history? Remove us. */ cache_history_remove( vi ); /* Drop any refs we may have left dangling. */ call_drop_refs( vi ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } /* Junk stuff we may have attached to vargv. */ static void call_vargv_free( im_function *fn, im_object *vargv ) { int i; /* Free any CALL args we built and haven't used. */ for( i = 0; i < fn->argc; i++ ) { im_type_desc *ty = fn->argv[i].desc; im_object *obj = vargv[i]; CallArgumentType vt; /* Make sure we don't damage any error message we might * have. */ error_block(); vt = call_lookup_type( ty->type ); error_unblock(); switch( vt ) { case CALL_NONE: /* IM_TYPE_DISPLAY */ case CALL_DOUBLE: case CALL_INT: case CALL_COMPLEX: case CALL_GVALUE: case CALL_INTERPOLATE: case CALL_IMAGE: /* Do nothing. */ break; case CALL_STRING: IM_FREE( obj ); break; case CALL_IMAGEVEC: IM_FREE( ((im_imagevec_object *) obj)->vec ); break; case CALL_DOUBLEVEC: IM_FREE( ((im_doublevec_object *) obj)->vec ); break; case CALL_INTVEC: IM_FREE( ((im_intvec_object *) obj)->vec ); break; case CALL_DMASK: IM_FREE( ((im_mask_object *) obj)->name ); IM_FREEF( im_free_dmask, ((im_mask_object *) obj)->mask ); break; case CALL_IMASK: IM_FREE( ((im_mask_object *) obj)->name ); IM_FREEF( im_free_imask, ((im_mask_object *) obj)->mask ); break; default: g_assert( FALSE ); } } } static void call_info_finalize( GObject *gobject ) { CallInfo *vi; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_CALL_INFO( gobject ) ); vi = CALL_INFO( gobject ); #ifdef DEBUG_LEAK call_info_all = g_slist_remove( call_info_all, vi ); #endif /*DEBUG_LEAK*/ if( vi->vargv ) { call_vargv_free( vi->fn, vi->vargv ); im_free_vargv( vi->fn, vi->vargv ); IM_FREE( vi->vargv ); } G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void call_info_info( iObject *iobject, VipsBuf *buf ) { CallInfo *vi = CALL_INFO( iobject ); vips_buf_appendf( buf, "call_info_info: (%p) %s \"%s\"\n", vi, G_OBJECT_TYPE_NAME( vi ), NN( IOBJECT( vi )->name ) ); } static void call_info_class_init( CallInfoClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = call_info_dispose; gobject_class->finalize = call_info_finalize; iobject_class->info = call_info_info; } static void call_info_init( CallInfo *vi ) { int i; vi->name = NULL; vi->fn = NULL; vi->rc = NULL; vi->vargv = NULL; vi->nargs = 0; vi->nres = 0; vi->nires = 0; vi->ninii = 0; vi->noutii = 0; vi->use_lut = FALSE; /* Set this properly later */ vi->found_hash = FALSE; vi->in_cache = FALSE; vi->must_drop = FALSE; #ifdef DEBUG_LEAK call_info_all = g_slist_prepend( call_info_all, vi ); #endif /*DEBUG_LEAK*/ for( i = 0; i < MAX_CALL_ARGS; i++ ) { vi->outii_destroy_sid[i] = 0; vi->inii_destroy_sid[i] = 0; vi->inii_invalidate_sid[i] = 0; } } GType call_info_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( CallInfoClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) call_info_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( CallInfo ), 32, /* n_preallocs */ (GInstanceInitFunc) call_info_init, }; type = g_type_register_static( TYPE_IOBJECT, "CallInfo", &info, 0 ); } return( type ); } static CallInfo * call_new( Reduce *rc, im_function *fn ) { CallInfo *vi; int i; g_assert( fn->argc < MAX_CALL_ARGS - 1 ); if( !fn || !(vi = CALL_INFO( g_object_new( TYPE_CALL_INFO, NULL ) )) ) return( NULL ); vi->name = fn->name; vi->fn = fn; vi->rc = rc; /* Look over the args ... count the number of inputs we need, and * the number of outputs we generate. Note the position of each. */ for( i = 0; i < vi->fn->argc; i++ ) { im_type_desc *ty = vi->fn->argv[i].desc; if( call_type_makes_output( ty ) ) { vi->outpos[vi->nres] = i; vi->nres += 1; /* Image output. */ if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) vi->nires += 1; } if( call_type_needs_input( ty ) ) { vi->inpos[vi->nargs] = i; vi->nargs += 1; } } /* Make the call spine, alloc memory. */ if( !(vi->vargv = IM_ARRAY( NULL, vi->fn->argc + 1, im_object )) || im_allocate_vargv( vi->fn, vi->vargv ) ) { call_error( vi ); g_object_unref( vi ); return( NULL ); } return( vi ); } /* Add another ii to outii. */ static gboolean call_add_output_ii( CallInfo *vi, Imageinfo *ii ) { if( vi->noutii > MAX_CALL_ARGS ) { call_error_toomany( vi ); return( FALSE ); } vi->outii[vi->noutii] = ii; vi->noutii += 1; /* We hold a ref to the ii until the call is done and the result * written back to nip2. If we cache the result, we make a new * weakref. */ managed_dup_nonheap( MANAGED( ii ) ); vi->must_drop = TRUE; return( TRUE ); } /* Init an output slot in vargv. */ static gboolean call_build_output( CallInfo *vi, int i ) { im_type_desc *ty = vi->fn->argv[i].desc; /* Provide output objects for the function to write to. */ switch( call_lookup_type( ty->type ) ) { case CALL_DOUBLE: case CALL_INT: case CALL_COMPLEX: case CALL_STRING: break; case CALL_IMAGE: { Imageinfo *ii; if( !(ii = imageinfo_new_temp( main_imageinfogroup, vi->rc->heap, NULL, "p" )) || !call_add_output_ii( vi, ii ) || !(vi->vargv[i] = imageinfo_get( FALSE, ii )) ) return( FALSE ); break; } case CALL_DMASK: case CALL_IMASK: { im_mask_object *mo = vi->vargv[i]; mo->mask = NULL; mo->name = im_strdup( NULL, "" ); break; } case CALL_GVALUE: { GValue *value = vi->vargv[i]; memset( value, 0, sizeof( GValue ) ); break; } case CALL_DOUBLEVEC: case CALL_INTVEC: { /* intvec is also int + pointer. */ im_doublevec_object *dv = vi->vargv[i]; dv->n = 0; dv->vec = NULL; break; } default: g_assert( FALSE ); } return( TRUE ); } static gboolean call_build_inputva( CallInfo *vi, int i, va_list *ap ) { im_type_desc *ty = vi->fn->argv[i].desc; switch( call_lookup_type( ty->type ) ) { case CALL_DOUBLE: { double v = va_arg( *ap, double ); #ifdef DEBUG printf( "%g\n", v ); #endif /*DEBUG*/ *((double*)vi->vargv[i]) = v; if( trace_flags & TRACE_VIPS ) vips_buf_appendf( trace_current(), "%g ", v ); break; } case CALL_INT: { int v = va_arg( *ap, int ); #ifdef DEBUG printf( "%d\n", v ); #endif /*DEBUG*/ *((int*)vi->vargv[i]) = v; if( trace_flags & TRACE_VIPS ) vips_buf_appendf( trace_current(), "%d ", v ); break; } case CALL_GVALUE: { GValue *value = va_arg( *ap, GValue * ); #ifdef DEBUG printf( "gvalue %p\n", value ); #endif /*DEBUG*/ vi->vargv[i] = value; if( trace_flags & TRACE_VIPS ) { vips_buf_appendgv( trace_current(), value ); vips_buf_appends( trace_current(), " " ); } break; } case CALL_INTERPOLATE: { VipsInterpolate *value = va_arg( *ap, VipsInterpolate * ); #ifdef DEBUG printf( "interpolate %p\n", value ); #endif /*DEBUG*/ vi->vargv[i] = value; if( trace_flags & TRACE_VIPS ) { vips_object_to_string( VIPS_OBJECT( value ), trace_current() ); vips_buf_appends( trace_current(), " " ); } break; } case CALL_IMAGE: { Imageinfo *ii = va_arg( *ap, Imageinfo * ); #ifdef DEBUG printf( "imageinfo %p\n", ii ); #endif /*DEBUG*/ if( !call_add_input_ii( vi, ii ) ) return( FALSE ); /* Filled in later. */ vi->vargv[i] = NULL; if( trace_flags & TRACE_VIPS ) { VipsBuf *buf = trace_current(); if( ii && ii->im ) { vips_buf_appends( buf, "<" ); vips_buf_appendf( buf, _( "image \"%s\"" ), ii->im->filename ); vips_buf_appends( buf, "> " ); } else { vips_buf_appends( buf, "<" ); vips_buf_appends( buf, _( "no image" ) ); vips_buf_appends( buf, "> " ); } } break; } case CALL_DOUBLEVEC: { int n = va_arg( *ap, int ); double *vec = va_arg( *ap, double * ); #ifdef DEBUG { int i; for( i = 0; i < n; i++ ) printf( "%g, ", vec[i] ); printf( "\n" ); } #endif /*DEBUG*/ if( call_make_doublevec( vi->vargv[i], n, vec ) ) return( FALSE ); if( trace_flags & TRACE_VIPS ) { VipsBuf *buf = trace_current(); int i; vips_buf_appendf( buf, "<" ); vips_buf_appendf( buf, _( "doublevec" ) ); for( i = 0; i < n; i++ ) vips_buf_appendf( buf, " %g", vec[i] ); vips_buf_appends( buf, "> " ); } break; } /* FIXME ... add intvec perhaps */ case CALL_IMAGEVEC: { int n = va_arg( *ap, int ); Imageinfo **vec = va_arg( *ap, Imageinfo ** ); #ifdef DEBUG { int i; for( i = 0; i < n; i++ ) printf( "%p, ", vec[i] ); printf( "\n" ); } #endif /*DEBUG*/ if( call_make_imagevec( vi->vargv[i], n ) ) return( FALSE ); for( i = 0; i < n; i++ ) if( !call_add_input_ii( vi, vec[i] ) ) return( FALSE ); if( trace_flags & TRACE_VIPS ) { VipsBuf *buf = trace_current(); int i; vips_buf_appendf( buf, "<" ); vips_buf_appendf( buf, _( "imagevec" ) ); for( i = 0; i < n; i++ ) { vips_buf_appendf( buf, " <" ); vips_buf_appendf( buf, _( "image \"%s\"" ), vec[i]->im->filename ); vips_buf_appendf( buf, ">" ); } vips_buf_appends( buf, "> " ); } break; } default: g_assert( FALSE ); } return( TRUE ); } /* Fill an argument vector from the C stack. */ static gboolean call_fillva( CallInfo *vi, va_list *ap ) { int i; g_assert( vi->ninii == 0 ); g_assert( vi->noutii == 0 ); for( i = 0; i < vi->fn->argc; i++ ) { im_type_desc *ty = vi->fn->argv[i].desc; #ifdef DEBUG printf( "call_fillva: arg[%d] (%s) = ", i, ty->type ); #endif /*DEBUG*/ if( call_type_makes_output( ty ) ) { if( !call_build_output( vi, i ) ) return( FALSE ); #ifdef DEBUG printf( " output\n" ); #endif /*DEBUG*/ } if( strcmp( ty->type, IM_TYPE_DISPLAY ) == 0 ) { /* DISPLAY argument ... just IM_TYPE_sRGB. */ vi->vargv[i] = im_col_displays( 7 ); #ifdef DEBUG printf( " display\n" ); #endif /*DEBUG*/ } if( call_type_needs_input( ty ) ) { if( !call_build_inputva( vi, i, ap ) ) return( FALSE ); } } /* Every output ii depends upon all of the input ii. */ for( i = 0; i < vi->noutii; i++ ) managed_sub_add_all( MANAGED( vi->outii[i] ), vi->ninii, (Managed **) vi->inii ); #ifdef DEBUG printf( "call_fill_spine: reffed %d in\n", vi->ninii ); printf( "call_fill_spine: created %d out\n", vi->noutii ); #endif /*DEBUG*/ return( TRUE ); } static gboolean callva_sub( Reduce *rc, const char *name, PElement *out, va_list *ap ) { CallInfo *vi; gboolean result; if( trace_flags & TRACE_VIPS ) trace_push(); if( !(vi = call_new( rc, im_find_function( name ) )) ) return( FALSE ); if( trace_flags & TRACE_VIPS ) vips_buf_appendf( trace_current(), "\"%s\" ", vi->name ); result = TRUE; if( !call_fillva( vi, ap ) ) result = FALSE; if( trace_flags & TRACE_VIPS ) vips_buf_appends( trace_current(), " ->\n" ); if( result && ( !(vi = cache_dispatch( vi, out )) || !call_write_result( vi, out ) ) ) result = FALSE; if( trace_flags & TRACE_VIPS ) { trace_result( TRACE_VIPS, out ); trace_pop(); } if( vi ) { /* We must drop refs explicitly, since this unref might not * dispose the vi. */ call_drop_refs( vi ); g_object_unref( vi ); } return( result ); } /* Call a CALL function picking up args from the function call. */ void callva( Reduce *rc, PElement *out, const char *name, ... ) { va_list ap; gboolean result; #ifdef DEBUG printf( "** callva: starting for %s\n", name ); #endif /*DEBUG*/ va_start( ap, name ); result = callva_sub( rc, name, out, &ap ); va_end( ap ); #ifdef DEBUG printf( "callva: done\n" ); #endif /*DEBUG*/ if( !result ) reduce_throw( rc ); } /* Fill an argument vector from our stack frame. Number of args already * checked. */ static gboolean call_fill_spine( CallInfo *vi, HeapNode **arg ) { int i, j; g_assert( vi->ninii == 0 ); g_assert( vi->noutii == 0 ); /* Fully reduce all arguments. Once we've done this, we can be sure * there will not be a GC while we gather, and therefore that no * pointers will become invalid during this call. */ for( i = 0; i < vi->nargs; i++ ) { PElement rhs; PEPOINTRIGHT( arg[i], &rhs ); if( !heap_reduce_strict( &rhs ) ) return( FALSE ); } for( j = 0, i = 0; i < vi->fn->argc; i++ ) { im_type_desc *ty = vi->fn->argv[i].desc; if( call_type_makes_output( ty ) ) if( !call_build_output( vi, i ) ) return( FALSE ); if( strcmp( ty->type, IM_TYPE_DISPLAY ) == 0 ) { /* Special DISPLAY argument - don't fetch another ip * argument for it. */ (void) call_fromip( vi, i, NULL ); } if( call_type_needs_input( ty ) ) { PElement rhs; /* Convert ip type to CALL type. */ PEPOINTRIGHT( arg[vi->nargs - j - 1], &rhs ); if( !call_fromip( vi, i, &rhs ) ) { call_error_arg( vi, arg, j ); return( FALSE ); } j += 1; } } /* Every output ii depends upon all of the input ii. */ for( i = 0; i < vi->noutii; i++ ) managed_sub_add_all( MANAGED( vi->outii[i] ), vi->ninii, (Managed **) vi->inii ); #ifdef DEBUG printf( "call_fill_spine: reffed %d inii\n", vi->ninii ); printf( "call_fill_spine: created %d outii\n", vi->noutii ); #endif /*DEBUG*/ return( TRUE ); } static gboolean call_spine_sub( Reduce *rc, const char *name, im_function *fn, PElement *out, HeapNode **arg ) { CallInfo *vi; gboolean result; #ifdef DEBUG printf( "** call_spine: starting for %s\n", name ); #endif /*DEBUG*/ if( !(vi = call_new( rc, fn )) ) return( FALSE ); if( trace_flags & TRACE_VIPS ) { VipsBuf *buf = trace_push(); vips_buf_appendf( buf, "\"%s\" ", name ); trace_args( arg, vi->nargs ); } result = TRUE; if( !call_fill_spine( vi, arg ) || !(vi = cache_dispatch( vi, out )) || !call_write_result( vi, out ) ) result = FALSE; if( trace_flags & TRACE_VIPS ) { trace_result( TRACE_VIPS, out ); trace_pop(); } if( vi ) { /* We must drop refs explicitly, since this unref might not * dispose the vi. */ call_drop_refs( vi ); g_object_unref( vi ); } #ifdef DEBUG printf( "call_spine: done\n" ); #endif /*DEBUG*/ return( result ); } /* Call a CALL function, pick up args from the graph. */ void call_spine( Reduce *rc, const char *name, HeapNode **arg, PElement *out ) { if( !call_spine_sub( rc, name, im_find_function( name ), out, arg ) ) reduce_throw( rc ); } /* As an ActionFn. */ void call_run( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out, im_function *function ) { if( !call_spine_sub( rc, name, function, out, arg ) ) reduce_throw( rc ); } ================================================ FILE: src/call.h ================================================ /* Call vips functions from the graph reducer. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with CALL - http://www.vips.ecs.soton.ac.uk */ /* Maxiumum number of args to a CALL function. */ #define MAX_CALL_ARGS (100) /* Maximum length of a vector we pass to INTVEC etc. */ #define MAX_VEC (10000) typedef enum _CallArgumentType { CALL_NONE = -1, CALL_DOUBLE = 0, CALL_INT, CALL_COMPLEX, CALL_STRING, CALL_IMAGE, CALL_DOUBLEVEC, CALL_DMASK, CALL_IMASK, CALL_IMAGEVEC, CALL_INTVEC, CALL_GVALUE, CALL_INTERPOLATE } CallArgumentType; #define TYPE_CALL_INFO (call_info_get_type()) #define CALL_INFO( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_CALL_INFO, CallInfo )) #define CALL_INFO_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_CALL_INFO, CallInfoClass)) #define IS_CALL_INFO( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_CALL_INFO )) #define IS_CALL_INFO_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_CALL_INFO )) #define CALL_INFO_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_CALL_INFO, CallInfoClass )) /* Stuff we hold about a call to a CALL function. */ typedef struct _CallInfo { iObject parent_object; /* Environment. */ const char *name; im_function *fn; /* Function we call */ Reduce *rc; /* RC we run inside */ /* Args we build. Images in vargv are IMAGE* pointers. */ im_object *vargv; /* vargv we build for CALL */ int nargs; /* Number of args needed from ip */ int nres; /* Number of objects we write back */ int nires; /* Number of images we write back */ int inpos[MAX_CALL_ARGS]; /* Positions of inputs */ int outpos[MAX_CALL_ARGS]; /* Positions of outputs */ /* Input images. Need to track "destroy" on each one (and kill us * in turn). * * RW images are a bit different. These are really output images (we * create the image that gets passed to the operation, just like * output images), but it's a "t" image and we im_copy() an input to * it to init it. * * So RW images appear in both inii and outii, but we don't look for * destroy for it. */ int ninii; Imageinfo *inii[MAX_CALL_ARGS]; unsigned int inii_destroy_sid[MAX_CALL_ARGS]; unsigned int inii_invalidate_sid[MAX_CALL_ARGS]; /* Output images. */ int noutii; Imageinfo *outii[MAX_CALL_ARGS]; unsigned int outii_destroy_sid[MAX_CALL_ARGS]; gboolean use_lut; /* TRUE for using a lut */ /* Cache hash code here. */ unsigned int hash; gboolean found_hash; /* Set if we're in the history cache. */ gboolean in_cache; /* Set if we hold refs in inii/outii that must be dropped. */ gboolean must_drop; } CallInfo; typedef struct _CallInfoClass { iObjectClass parent_class; } CallInfoClass; extern GSList *call_info_all; CallArgumentType call_lookup_type( im_arg_type type ); void call_error( CallInfo *vi ); void call_error_toomany( CallInfo *vi ); GType call_info_get_type( void ); void call_check_all_destroyed( void ); gboolean call_type_needs_input( im_type_desc *ty ); gboolean call_type_makes_output( im_type_desc *ty ); gboolean call_is_callable( im_function *fn ); int call_n_args( im_function *fn ); void call_usage( VipsBuf *buf, im_function *fn ); void call_spine( Reduce *rc, const char *name, HeapNode **arg, PElement *out ); void call_run( Reduce *rc, Compile *compile, int op, const char *name, HeapNode **arg, PElement *out, im_function *function ); void callva( Reduce *rc, PElement *out, const char *name, ... ); ================================================ FILE: src/class.c ================================================ /* Class functions ... really part of heap.c, but split out here to make it * more manageable. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG_MEMBER #define DEBUG_VERBOSE #define DEBUG #define DEBUG_BUILD */ static gboolean class_is_class( PElement *instance ) { if( !PEISCLASS( instance ) ) { char txt[50]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( !itext_value( reduce_context, &buf, instance ) ) return( FALSE ); error_top( _( "Bad argument." ) ); error_sub( _( "Object %s is not a class." ), vips_buf_all( &buf ) ); return( FALSE ); } return( TRUE ); } Compile * class_get_compile( PElement *instance ) { if( !class_is_class( instance ) ) return( NULL ); return( PEGETCLASSCOMPILE( instance ) ); } /* Look up "super" in a class ... try to do it quickly. */ gboolean class_get_super( PElement *instance, PElement *out ) { Compile *compile; if( !(compile = class_get_compile( instance )) ) return( FALSE ); g_assert( compile->super ); return( class_get_symbol( instance, compile->super, out ) ); } void * class_map( PElement *instance, class_map_fn fn, void *a, void *b ) { PElement member; HeapNode *p; if( !PEISCLASS( instance ) ) return( NULL ); /* Loop over the instance member list. */ PEGETCLASSMEMBER( &member, instance ); if( !PEISELIST( &member ) ) for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) { HeapNode *hn; PElement s, v; Symbol *sym; void *result; /* Get the sym/value pair, get the sym. */ hn = GETLEFT( p ); PEPOINTLEFT( hn, &s ); PEPOINTRIGHT( hn, &v ); sym = PEGETSYMREF( &s ); if( (result = fn( sym, &v, a, b )) ) return( result ); } return( NULL ); } /* Look up a member in a class instance by name. If lookup fails in this * instance, try the superclass. Don't search secrets. Point sym and value * at the symbol we found and its value. sym can be NULL for no result * required. */ gboolean class_get_member( PElement *instance, const char *name, Symbol **sym_out, PElement *out ) { PElement member; PElement super; HeapNode *p; #ifdef DEBUG_MEMBER printf( "class_get_member: looking up \"%s\" in class ", name ); pgraph( instance ); #endif /*DEBUG_MEMBER*/ if( !class_is_class( instance ) ) return( FALSE ); /* Search this instance member list. */ PEGETCLASSMEMBER( &member, instance ); if( !PEISELIST( &member ) ) for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) { HeapNode *hn; PElement s; Symbol *sym; /* Get the sym/value pair, get the sym. */ hn = GETLEFT( p ); PEPOINTLEFT( hn, &s ); /* Match? */ sym = PEGETSYMREF( &s ); if( strcmp( IOBJECT( sym )->name, name ) == 0 ) { /* Found! */ PEPOINTRIGHT( hn, out ); if( sym_out ) *sym_out = sym; #ifdef DEBUG_MEMBER printf( "class_get_member: found: " ); pgraph( out ); #endif /*DEBUG_MEMBER*/ return( TRUE ); } } /* Nope ... try the superclass. */ if( !class_get_super( instance, &super ) || !PEISELIST( &super ) ) { /* FIXME ... gcc 2.95.2 gets this wrong, tries to eliminate the tail recursion with -O2 and makes bad code ... guess how long that took to find ... put this back at some point return( class_get_member( &super, name, sym_out, value ) ); */ gboolean result = class_get_member( &super, name, sym_out, out ); return( result ); } error_top( _( "Member not found." ) ); error_sub( _( "Member \"%s\" not found in class \"%s\"." ), name, IOBJECT( PEGETCLASSCOMPILE( instance )->sym )->name ); return( FALSE ); } /* Look up a symbol in a class. Write to out, or FALSE for not found. Look up * by symbol pointer. Search secrets as well. Try the superclass if lookup * fails. */ gboolean class_get_symbol( PElement *instance, Symbol *sym, PElement *out ) { HeapNode *p; PElement secret; PElement super; #ifdef DEBUG_MEMBER printf( "class_get_symbol: looking up " ); symbol_name_print( sym ); printf( "in class " ); pgraph( instance ); #endif /*DEBUG_MEMBER*/ if( !class_is_class( instance ) ) return( FALSE ); PEGETCLASSSECRET( &secret, instance ); if( PEISNODE( &secret ) ) for( p = PEGETVAL( &secret ); p; p = GETRIGHT( p ) ) { PElement s; HeapNode *hn; /* Get the sym/value pair, get the sym. */ hn = GETLEFT( p ); PEPOINTLEFT( hn, &s ); /* Match? */ if( PEGETSYMREF( &s ) == sym ) { /* Found! */ PEPOINTRIGHT( hn, out ); #ifdef DEBUG_MEMBER printf( "class_get_symbol: found: " ); pgraph( out ); #endif /*DEBUG_MEMBER*/ return( TRUE ); } } /* Nope ... try the superclass. */ if( !class_get_super( instance, &super ) || !PEISELIST( &super ) ) { /* FIXME ... gcc 2.95.2 gets this wrong, tries to eliminate the tail recursion with -O2 and makes bad code ... guess how long that took to find ... put this back at some point return( class_get_member( &super, name, out ) ); */ gboolean result = class_get_symbol( &super, sym, out ); return( result ); } return( FALSE ); } /* Search back up the inheritance tree for an exact instance of this * class. */ gboolean class_get_exact( PElement *instance, const char *name, PElement *out ) { PElement pe; pe = *instance; while( !reduce_is_instanceof_exact( reduce_context, name, &pe ) ) { if( !class_get_super( &pe, &pe ) || PEISELIST( &pe ) ) return( FALSE ); } *out = pe; return( TRUE ); } /* Stuff we need for class build. */ typedef struct { Heap *heap; /* Heap to build on */ Symbol *sym; /* Sym we are local to */ PElement *arg; /* Args to constructor */ PElement *this; /* Base of instance we are building */ int i; /* Index in arg list */ Compile *compile; /* Compile for our class */ } ClassBuildInfo; /* Member sym of class pbi->sym needs secret as an argument ... add it! */ static gboolean class_member_secret( ClassBuildInfo *pbi, Symbol *sym, GSList *secret, PElement *out ) { Symbol *ssym; Heap *heap = pbi->heap; HeapNode *apl; if( !secret ) return( TRUE ); ssym = SYMBOL( secret->data ); /* Make function application for this member. */ if( NEWNODE( heap, apl ) ) return( FALSE ); apl->type = TAG_APPL; PEPUTLEFT( apl, out ); /* Is the secret "this"? Easy. */ if( ssym == pbi->sym->expr->compile->this ) { PEPUTRIGHT( apl, pbi->this ); } else { /* Look up ssym in pbi->sym's secrets ... should be there * somewhere. Use it's index to find the pbi->arg[] we need. */ int pos = g_slist_index( pbi->sym->expr->compile->secret, ssym ); /* FIXME ... may not be if we've regenerated one of these * stupid things :-( change this so we always go through * 'this'. */ if( pos < 0 || pos >= pbi->sym->expr->compile->nsecret ) { error_top( _( "No such secret." ) ); error_sub( _( "Editing local classes which reference " "non-local objects is a bit broken at the " "moment :-(" ) ); return( FALSE ); } PEPUTRIGHT( apl, &pbi->arg[pbi->sym->expr->compile->nsecret - pos - 1] ); } PEPUTP( out, ELEMENT_NODE, apl ); #ifdef DEBUG_VERBOSE { PElement p1; char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); PEPOINTRIGHT( apl, &p1 ); graph_pelement( pbi->heap, &buf, &p1, TRUE ); printf( "class_member_secret: secret arg " ); symbol_name_print( ssym ); printf( "to member " ); symbol_name_print( sym ); printf( "= %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_VERBOSE*/ return( class_member_secret( pbi, sym, secret->next, out ) ); } /* Add a member to a class. */ static void * add_class_member( Symbol *sym, ClassBuildInfo *pbi, PElement *out ) { Heap *heap = pbi->heap; HeapNode *base, *sv; PElement v; /* Is this something that should be part of a class. */ if( sym->type != SYM_VALUE ) return( NULL ); /* Make new class-local-list element for this local. */ if( NEWNODE( heap, base ) ) return( sym ); base->type = TAG_CONS; PPUTLEFT( base, ELEMENT_ELIST, NULL ); PEPUTRIGHT( base, out ); PEPUTP( out, ELEMENT_NODE, base ); /* Make sym/value pair for this local. */ if( NEWNODE( heap, sv ) ) return( sym ); sv->type = TAG_CONS; PPUT( sv, ELEMENT_SYMREF, sym, ELEMENT_SYMBOL, sym ); PPUTLEFT( base, ELEMENT_NODE, sv ); /* Build value ... apply args to the symbol. */ PEPOINTRIGHT( sv, &v ); if( !class_member_secret( pbi, sym, sym->expr->compile->secret, &v ) ) return( sym ); #ifdef DEBUG_VERBOSE { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, &v, TRUE ); printf( "add_class_member: member \"%s\" of class \"%s\" = %s\n", IOBJECT( sym )->name, IOBJECT( pbi->sym )->name, vips_buf_all( &buf ) ); } #endif /*DEBUG_VERBOSE*/ return( NULL ); } /* Add a symbol/value pair to a class. */ static gboolean add_class_svpair( ClassBuildInfo *pbi, Symbol *sym, PElement *val, PElement *out ) { Heap *heap = pbi->heap; HeapNode *base, *sv; #ifdef DEBUG_VERBOSE { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, val, TRUE ); printf( "add_class_svpair: adding parameter \"%s\" to class " "\"%s\" = %s\n", IOBJECT( sym )->name, IOBJECT( pbi->sym )->name, vips_buf_all( &buf ) ); } #endif /*DEBUG_VERBOSE*/ /* Make new class-local-list element for this parameter. */ if( NEWNODE( heap, base ) ) return( FALSE ); base->type = TAG_CONS; PPUTLEFT( base, ELEMENT_ELIST, NULL ); PEPUTRIGHT( base, out ); PEPUTP( out, ELEMENT_NODE, base ); /* Make sym/value pair for this parameter. */ if( NEWNODE( heap, sv ) ) return( FALSE ); sv->type = TAG_CONS; PPUTLEFT( sv, ELEMENT_SYMREF, sym ) PEPUTRIGHT( sv, val ); PPUTLEFT( base, ELEMENT_NODE, sv ); return( TRUE ); } /* Add a parameter (secret or real) to a class. */ static void * add_class_parameter( Symbol *sym, ClassBuildInfo *pbi, PElement *out ) { /* Add this symbol/value pair. */ if( !add_class_svpair( pbi, sym, &pbi->arg[pbi->i], out ) ) return( sym ); /* Move arg index on. */ pbi->i += 1; return( NULL ); } /* Add the name member ... build the name string carefully. */ static void * class_new_single_name( Heap *heap, PElement *pe, ClassBuildInfo *pbi, PElement *instance ) { Symbol *snm = compile_lookup( pbi->compile, MEMBER_NAME ); char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); /* Make class name string. */ symbol_qualified_name( pbi->sym, &buf ); PEPUTP( pe, ELEMENT_ELIST, NULL ); if( !heap_managedstring_new( heap, vips_buf_all( &buf ), pe ) ) return( heap ); /* Add as a member. */ if( !add_class_svpair( pbi, snm, pe, instance ) ) return( heap ); return( NULL ); } /* Make a single level class instance ... fn below then loops over a class * hierarchy with this. */ static gboolean class_new_single( Heap *heap, Compile *compile, PElement *arg, PElement *this, PElement *out ) { Symbol *sym = compile->sym; Symbol *sths = compile->this; HeapNode *base, *sm; PElement p1; ClassBuildInfo pbi; #ifdef DEBUG { int i; printf( "class_new_single: starting for " ); symbol_name_print( sym ); printf( "%d secrets, %d params\n", compile->nsecret, compile->nparam ); for( i = 0; i < compile->nsecret; i++ ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, &arg[i], TRUE ); printf( "\tsecret %2d = %s\n", i, vips_buf_all( &buf ) ); } for( i = 0; i < compile->nparam; i++ ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, &arg[i + compile->nsecret], TRUE ); printf( "\targ %2d = %s\n", i, vips_buf_all( &buf ) ); } } #endif /*DEBUG*/ /* Make class base. */ if( NEWNODE( heap, base ) ) return( FALSE ); base->type = TAG_CLASS; PPUT( base, ELEMENT_COMPILEREF, compile, ELEMENT_ELIST, NULL ); PEPUTP( out, ELEMENT_NODE, base ); /* Make node for holding secrets and members. */ if( NEWNODE( heap, sm ) ) return( FALSE ); sm->type = TAG_CONS; PPUT( sm, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PPUTRIGHT( base, ELEMENT_NODE, sm ); /* Build list of members. */ pbi.heap = heap; pbi.sym = sym; pbi.arg = arg; pbi.this = this; pbi.compile = compile; PEPOINTRIGHT( sm, &p1 ); if( icontainer_map_rev( ICONTAINER( compile ), (icontainer_map_fn) add_class_member, &pbi, &p1 ) ) return( FALSE ); /* Add name member. */ if( heap_safe_pointer( heap, (heap_safe_pointer_fn) class_new_single_name, &pbi, &p1, NULL, NULL ) ) return( FALSE ); /* Add this member. */ if( !add_class_svpair( &pbi, sths, this, &p1 ) ) return( FALSE ); /* Add class parameters to member list. */ pbi.i = 0; if( slist_map2_rev( compile->param, (SListMap2Fn) add_class_parameter, &pbi, &p1 ) ) return( FALSE ); /* Now ... secret list starts off pointing to head of member list. */ PEPUTLEFT( sm, &p1 ); /* Add all secret parameters to secret list. */ PEPOINTLEFT( sm, &p1 ); if( slist_map2_rev( compile->secret, (SListMap2Fn) add_class_parameter, &pbi, &p1 ) ) return( FALSE ); #ifdef DEBUG { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, out, TRUE ); printf( "class_new_single: built instance of " ); symbol_name_print( sym ); printf( ":\n%s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG*/ return( TRUE ); } /* Look at a scrap of graph and try to find a constructor it might be using. * This will only work for really basic functions :-( but it's enough to allow * us to pass extra secrets through the superclass. Used by (eg.) Colour when * it overrides Value and adds the colourspace arg. */ static Compile * class_guess_constructor( PElement *fn ) { if( PEISCONSTRUCTOR( fn ) ) return( PEGETCOMPILE( fn ) ); else if( PEISNODE( fn ) ) { HeapNode *hn = PEGETVAL( fn ); if( hn->type == TAG_APPL ) { PElement left; PEPOINTLEFT( hn, &left ); return( class_guess_constructor( &left ) ); } } return( NULL ); } /* Look at arg0 and try to extract the arguments (all the RHS of the @ nodes). * Return the number of args we found, or -1 if we find crazy stuff. */ static int class_guess_args( PElement arg[], PElement *fn ) { if( PEISCONSTRUCTOR( fn ) ) return( 0 ); else if( PEISNODE( fn ) ) { PElement left; int i; PEPOINTLEFT( PEGETVAL( fn ), &left ); if( (i = class_guess_args( arg, &left )) == -1 ) return( -1 ); if( i >= MAX_SYSTEM ) { error_top( _( "Too many arguments." ) ); error_sub( _( "You can't have more than %d " "arguments to a superclass constructor." ), MAX_SYSTEM ); return( -1 ); } PEPOINTRIGHT( PEGETVAL( fn ), &arg[i] ); return( i + 1 ); } else return( -1 ); } static void * class_new_super_sub( Heap *heap, PElement *p1, Compile *compile, PElement *arg, PElement *this, PElement *super ) { /* Build the superclass ... we overwrite the super * list with the constructed class, so make a copy of * the pointer to stop it being GCed. */ PEPUTPE( p1, super ); if( !class_new_single( heap, compile, arg, this, super ) ) return( heap ); return( NULL ); } /* Clone a class instance. Copy pointers to the the args, secrets and super; * rebuild with the specified "this". Instance and out can be equal. */ static gboolean class_clone_super( Heap *heap, Compile *compile, PElement *instance, PElement *this, PElement *out ) { PElement arg[MAX_SYSTEM]; const int nargs = compile->nsecret + compile->nparam; PElement secret; int i; g_assert( nargs <= MAX_SYSTEM ); #ifdef DEBUG_VERBOSE { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, instance, TRUE ); printf( "class_new_clone: about to clone \"%s\": %s\n", IOBJECT( compile->sym )->name, vips_buf_all( &buf ) ); } #endif /*DEBUG_VERBOSE*/ /* Pull out values of secrets and class args into arg[]. */ PEGETCLASSSECRET( &secret, instance ); for( i = 0; i < nargs; i++ ) { HeapNode *hn = PEGETVAL( &secret ); HeapNode *sv = GETLEFT( hn ); int index = nargs - i - 1; PEPOINTRIGHT( sv, &arg[index] ); PEPOINTRIGHT( hn, &secret ); } /* Build class again. */ return( class_new_single( heap, compile, arg, this, out ) ); } static void * class_clone_super_sub( Heap *heap, PElement *p1, Compile *compile, PElement *instance, PElement *this, PElement *out ) { /* instance and out can point to the same node, so save a pointer to * instance to stop it being GCed. */ PEPUTPE( p1, instance ); if( !class_clone_super( heap, compile, instance, this, out ) ) return( heap ); return( NULL ); } /* Does this class have a "super"? Build it and recurse. */ gboolean class_new_super( Heap *heap, Compile *compile, PElement *this, PElement *instance ) { PElement super; if( compile->has_super && class_get_super( instance, &super ) ) { Compile *super_compile; int len, fn_len; PElement arg0; /* It must be a list whose first element is the superclass * constructor, or a partially parameterised constructor, or * the superclass itself (if it has already * been constructed, or has no args). Other elements in the * list are the remaining args. * * We keep the list form, since we want to not build the * superclass until now if we can help it ... otherwise we * have to construct once, then construct again when we clone. */ if( (len = heap_list_length( &super )) < 1 || !heap_list_index( &super, 0, &arg0 ) || !heap_reduce_strict( &arg0 ) ) return( FALSE ); if( (super_compile = class_guess_constructor( &arg0 )) ) { PElement fn_arg[MAX_SYSTEM]; PElement arg[MAX_SYSTEM]; int i; /* How many function args are there? */ if( (fn_len = class_guess_args( fn_arg, &arg0 )) < 0 ) return( FALSE ); /* Check total arg count. */ if( super_compile->nsecret != 0 ) { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); slist_map2( super_compile->secret, (SListMap2Fn) symbol_name_error, &buf, NULL ); error_top( _( "Bad superclass." ) ); error_sub( _( "Superclass constructor \"%s\" " "refers to non-local symbols %s" ), symbol_name( super_compile->sym ), vips_buf_all( &buf ) ); return( FALSE ); } if( len - 1 + fn_len != super_compile->nparam ) { error_top( _( "Wrong number of arguments." ) ); error_sub( _( "Superclass constructor \"%s\" " "expects %d arguments, not %d." ), symbol_name( super_compile->sym ), super_compile->nparam, len - 1 + fn_len ); return( FALSE ); } /* Grab the explicit args from the super list. */ for( i = 0; i < len - 1; i++ ) { if( !heap_list_index( &super, len - 1 - i, &arg[i] ) ) return( FALSE ); } /* Append the function args, but reverse them as we * go so we get most-nested arg last. */ for( i = 0; i < fn_len; i++ ) arg[i + len - 1] = fn_arg[fn_len - 1 - i]; /* Build the superclass ... we overwrite the super * list with the constructed class, so make a copy of * the pointer to stop it being GCed. */ if( heap_safe_pointer( heap, (heap_safe_pointer_fn) class_new_super_sub, super_compile, arg, this, &super ) ) return( FALSE ); } else if( PEISCLASS( &arg0 ) ) { /* Super is a constructed class ... clone it, but with * our "this" in there. Slow, but useful. */ super_compile = PEGETCLASSCOMPILE( &arg0 ); if( heap_safe_pointer( heap, (heap_safe_pointer_fn) class_clone_super_sub, super_compile, &arg0, this, &super ) ) return( FALSE ); } else { char txt1[300]; VipsBuf buf1 = VIPS_BUF_STATIC( txt1 ); char txt2[300]; VipsBuf buf2 = VIPS_BUF_STATIC( txt2 ); error_top( _( "Bad superclass." ) ); itext_value( reduce_context, &buf1, &arg0 ); vips_buf_appendf( &buf2, _( "First element in superclass of \"%s\" " "must be class or constructor." ), symbol_name( compile->sym ) ); vips_buf_appendf( &buf2, "\n" ); vips_buf_appendf( &buf2, _( "You passed:" ) ); error_sub( "%s\n %s", vips_buf_all( &buf2 ), vips_buf_all( &buf1 ) ); return( FALSE ); } /* And recursively build any superclasses. */ if( !class_new_super( heap, super_compile, this, &super ) ) return( FALSE ); } return( TRUE ); } /* Make a class instance. */ gboolean class_new( Heap *heap, Compile *compile, HeapNode **arg, PElement *out ) { int i; PElement pe_arg[MAX_SYSTEM]; /* Make a set of arg pointers. */ if( compile->nparam + compile->nsecret >= MAX_SYSTEM ) { error_top( _( "Too many arguments." ) ); error_sub( _( "Too many arguments to class constructor \"%s\". " "No more than %d arguments are supported." ), symbol_name( compile->sym ), MAX_SYSTEM ); return( FALSE ); } for( i = 0; i < compile->nparam + compile->nsecret; i++ ) { PEPOINTRIGHT( arg[i], &pe_arg[i] ); } /* Build the base instance. */ if( !class_new_single( heap, compile, pe_arg, out, out ) ) return( FALSE ); /* And recursively build any superclasses. */ if( !class_new_super( heap, compile, out, out ) ) return( FALSE ); #ifdef DEBUG_BUILD { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, out, TRUE ); printf( "class_new: built instance of \"%s\": %s\n", IOBJECT( compile->sym )->name, vips_buf_all( &buf ) ); } #endif /*DEBUG_BUILD*/ return( TRUE ); } /* Clone a class instance. Copy pointers to the the args, secrets and super; * regenerate all the members. instance and out can be equal. */ gboolean class_clone_args( Heap *heap, PElement *instance, PElement *out ) { HeapNode *arg[MAX_SYSTEM]; Compile *compile = PEGETCLASSCOMPILE( instance ); const int nargs = compile->nsecret + compile->nparam; PElement secret; int i; g_assert( nargs <= MAX_SYSTEM ); #ifdef DEBUG_VERBOSE { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, instance, TRUE ); printf( "class_clone_args: about to clone \"%s\": %s\n", IOBJECT( compile->sym )->name, vips_buf_all( &buf ) ); } #endif /*DEBUG_VERBOSE*/ /* Pull out values of secrets and class args into RHS of arg[]. */ PEGETCLASSSECRET( &secret, instance ); for( i = 0; i < nargs; i++ ) { HeapNode *hn = PEGETVAL( &secret ); HeapNode *sv = GETLEFT( hn ); int index = nargs - i - 1; arg[index] = sv; PEPOINTRIGHT( hn, &secret ); } /* Build class again. */ return( class_new( heap, compile, &arg[0], out ) ); } /* Build a class instance picking parameters from C args ... handy for * making a new toggle instance on a click, for example. */ gboolean class_newv( Heap *heap, const char *name, PElement *out, ... ) { va_list ap; Symbol *sym; Compile *compile; HeapNode args[MAX_SYSTEM]; HeapNode *pargs[MAX_SYSTEM]; int i; if( !(sym = compile_lookup( symbol_root->expr->compile, name )) || !is_value( sym ) || !is_class( sym->expr->compile ) ) { error_top( _( "Class not found." ) ); error_sub( _( "Class \"%s\" not found." ), name ); return( FALSE ); } compile = sym->expr->compile; if( compile->nparam >= MAX_SYSTEM ) { error_top( _( "Too many arguments." ) ); error_sub( _( "Too many arguments to class constructor \"%s\". " "No more than %d arguments are supported." ), symbol_name( compile->sym ), MAX_SYSTEM ); return( FALSE ); } va_start( ap, out ); for( i = 0; i < compile->nparam; i++ ) { PElement *arg = va_arg( ap, PElement * ); PElement rhs; pargs[i] = &args[i]; PEPOINTRIGHT( pargs[i], &rhs ); PEPUTPE( &rhs, arg ); } va_end( ap ); return( class_new( heap, compile, &pargs[0], out ) ); } static void class_typecheck_error( PElement *instance, const char *name, const char *type ) { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); PElement val; vips_buf_appendf( &buf, _( "Member \"%s\" of class \"%s\" " "should be of type \"%s\", instead it's:" ), name, IOBJECT( PEGETCLASSCOMPILE( instance )->sym )->name, type ); vips_buf_appends( &buf, "\n " ); if( class_get_member( instance, name, NULL, &val ) && !itext_value( reduce_context, &buf, &val ) ) return; error_top( _( "Bad argument." ) ); error_sub( "%s", vips_buf_all( &buf ) ); } /* A function that gets a type from a class. */ typedef gboolean (*ClassGetFn)( PElement *, void * ); static gboolean class_get_member_check( PElement *instance, const char *name, const char *type, ClassGetFn fn, void *a ) { PElement val; if( !class_get_member( instance, name, NULL, &val ) ) return( FALSE ); if( !fn( &val, a ) ) { class_typecheck_error( instance, name, type ); return( FALSE ); } return( TRUE ); } gboolean class_get_member_bool( PElement *instance, const char *name, gboolean *out ) { return( class_get_member_check( instance, name, "bool", (ClassGetFn) heap_get_bool, out ) ); } gboolean class_get_member_real( PElement *instance, const char *name, double *out ) { return( class_get_member_check( instance, name, "real", (ClassGetFn) heap_get_real, out ) ); } gboolean class_get_member_int( PElement *instance, const char *name, int *out ) { double d; if( !class_get_member_check( instance, name, "real", (ClassGetFn) heap_get_real, &d ) ) return( FALSE ); *out = IM_RINT( d ); return( TRUE ); } gboolean class_get_member_class( PElement *instance, const char *name, const char *type, PElement *out ) { gboolean result; if( !class_get_member_check( instance, name, type, (ClassGetFn) heap_get_class, out ) ) return( FALSE ); if( !heap_is_instanceof( type, out, &result ) ) return( FALSE ); if( !result ) { class_typecheck_error( instance, name, type ); return( FALSE ); } return( TRUE ); } gboolean class_get_member_image( PElement *instance, const char *name, Imageinfo **out ) { return( class_get_member_check( instance, name, "image", (ClassGetFn) heap_get_image, out ) ); } gboolean class_get_member_lstring( PElement *instance, const char *name, GSList **labels ) { return( class_get_member_check( instance, name, "finite [[char]]", (ClassGetFn) heap_get_lstring, labels ) ); } gboolean class_get_member_string( PElement *instance, const char *name, char *buf, int sz ) { PElement val; if( !class_get_member( instance, name, NULL, &val ) ) return( FALSE ); if( !heap_get_string( &val, buf, sz ) ) { class_typecheck_error( instance, name, "finite [char]" ); return( FALSE ); } return( TRUE ); } gboolean class_get_member_instance( PElement *instance, const char *name, const char *klass, PElement *out ) { gboolean result; return( class_get_member( instance, name, NULL, out ) && heap_is_instanceof( klass, out, &result ) && result ); } gboolean class_get_member_matrix_size( PElement *instance, const char *name, int *xsize, int *ysize ) { PElement val; if( !class_get_member( instance, name, NULL, &val ) ) return( FALSE ); if( !heap_get_matrix_size( &val, xsize, ysize ) ) { class_typecheck_error( instance, name, "finite rectangular [[real]]" ); return( FALSE ); } return( TRUE ); } gboolean class_get_member_matrix( PElement *instance, const char *name, double *buf, int n, int *xsize, int *ysize ) { PElement val; if( !class_get_member( instance, name, NULL, &val ) ) return( FALSE ); if( !heap_get_matrix( &val, buf, n, xsize, ysize ) ) { class_typecheck_error( instance, name, "finite rectangular [[real]]" ); return( FALSE ); } return( TRUE ); } gboolean class_get_member_realvec( PElement *instance, const char *name, double *buf, int n, int *length ) { PElement val; int l; if( !class_get_member( instance, name, NULL, &val ) ) return( FALSE ); if( (l = heap_get_realvec( &val, buf, n )) < 0 ) { class_typecheck_error( instance, name, "finite [real]" ); return( FALSE ); } *length = l; return( TRUE ); } ================================================ FILE: src/class.h ================================================ /* Decls for class.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* The builtin member names we know about. */ #define MEMBER_BANDS "bands" #define MEMBER_CAPTION "caption" #define MEMBER_XCAPTION "xcaption" #define MEMBER_YCAPTION "ycaption" #define MEMBER_SERIES_CAPTIONS "series_captions" #define MEMBER_DISPLAY "display" #define MEMBER_CHECK "check" #define MEMBER_FILENAME "filename" #define MEMBER_FORMAT "format" #define MEMBER_FROM "from" #define MEMBER_HEIGHT "height" #define MEMBER_LABELS "labels" #define MEMBER_NAME "name" #define MEMBER_OFFSET "offset" #define MEMBER_SCALE "scale" #define MEMBER_SUPER "super" #define MEMBER_THIS "this" #define MEMBER_TO "to" #define MEMBER_VALUE "value" #define MEMBER_WIDTH "width" #define MEMBER_LEFT "left" #define MEMBER_TOP "top" #define MEMBER_IMAGE "image" #define MEMBER_OO_BINARY "oo_binary" #define MEMBER_OO_BINARY2 "oo_binary'" #define MEMBER_OO_UNARY "oo_unary" #define MEMBER_COLOUR_SPACE "colour_space" #define MEMBER_EXPR "expr" #define MEMBER_INTERVAL "interval" #define MEMBER_OPTIONS "options" #define MEMBER_VISLEVEL "_vislevel" #define MEMBER_ACTION "action" #define MEMBER_LABEL "label" #define MEMBER_ICON "icon" #define MEMBER_TOOLTIP "tooltip" /* The class names we know about. */ #define CLASS_SLIDER "Scale" #define CLASS_TOGGLE "Toggle" #define CLASS_IMAGE "Image" #define CLASS_COLOUR "Colour" #define CLASS_NUMBER "Number" #define CLASS_STRING "String" #define CLASS_OPTION "Option" #define CLASS_MATRIX "Matrix_vips" #define CLASS_ARROW "Arrow" #define CLASS_REGION "Region" #define CLASS_AREA "Area" #define CLASS_HGUIDE "HGuide" #define CLASS_VGUIDE "VGuide" #define CLASS_MARK "Mark" #define CLASS_POINT "Point" #define CLASS_PATHNAME "Pathname" #define CLASS_FONTNAME "Fontname" #define CLASS_SEPARATOR "Separator" #define CLASS_GROUP "Group" #define CLASS_LIST "List" #define CLASS_MENU "Menu" #define CLASS_MENUITEM "Menuitem" #define CLASS_MENUACTION "Menuaction" #define CLASS_MENUPULLRIGHT "Menupullright" #define CLASS_MENUSEPARATOR "Menuseparator" #define CLASS_EXPRESSION "Expression" #define CLASS_CLOCK "Clock" #define CLASS_REAL "Real" #define CLASS_VECTOR "Vector" #define CLASS_PLOT "Plot" /* What we loop over a class instance with. */ typedef void *(*class_map_fn)( Symbol *, PElement *, void *, void * ); Compile *class_get_compile( PElement *instance ); gboolean class_get_super( PElement *instance, PElement *out ); void *class_map( PElement *instance, class_map_fn fn, void *a, void *b ); gboolean class_get_member( PElement *instance, const char *name, Symbol **sym_out, PElement *value ); gboolean class_get_symbol( PElement *class, Symbol *sym, PElement *out ); gboolean class_get_exact( PElement *instance, const char *name, PElement *out ); gboolean class_new_super( Heap *heap, Compile *compile, PElement *this, PElement *instance ); gboolean class_new( Heap *heap, Compile *compile, HeapNode **args, PElement *out ); gboolean class_clone( Heap *heap, PElement *class, PElement *out ); gboolean class_clone_args( Heap *heap, PElement *klass, PElement *out ); gboolean class_newv( Heap *heap, const char *name, PElement *out, ... ); gboolean class_get_member_real( PElement *klass, const char *name, double *out ); gboolean class_get_member_int( PElement *instance, const char *name, int *out ); gboolean class_get_member_bool( PElement *klass, const char *name, gboolean *out ); gboolean class_get_member_image( PElement *instance, const char *name, Imageinfo **out ); gboolean class_get_member_class( PElement *instance, const char *name, const char *type, PElement *out ); gboolean class_get_member_lstring( PElement *instance, const char *name, GSList **labels ); gboolean class_get_member_string( PElement *klass, const char *name, char *buf, int sz ); gboolean class_get_member_instance( PElement *instance, const char *name, const char *klass, PElement *out ); gboolean class_get_member_matrix_size( PElement *instance, const char *name, int *xsize, int *ysize ); gboolean class_get_member_matrix( PElement *instance, const char *name, double *buf, int n, int *xsize, int *ysize ); gboolean class_get_member_realvec( PElement *instance, const char *name, double *buf, int n, int *length ); ================================================ FILE: src/classmodel.c ================================================ /* like a heapmodel, but we represent a class in the heap */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static HeapmodelClass *parent_class = NULL; void image_value_init( ImageValue *image, Classmodel *classmodel ) { image->ii = NULL; image->file_changed_sid = 0; image->classmodel = classmodel; } void image_value_destroy( ImageValue *image ) { FREESID( image->file_changed_sid, image->ii ); MANAGED_UNREF( image->ii ); } static void image_value_file_changed_cb( Imageinfo *ii, ImageValue *image ) { #ifdef DEBUG printf( "image_value_file_changed_cb: " ); iobject_print( IOBJECT( image->classmodel ) ); #endif /*DEBUG*/ if( CALC_RELOAD ) { Row *row = HEAPMODEL( image->classmodel )->row; (void) expr_dirty( row->expr, link_serial_new() ); symbol_recalculate_all(); } } void image_value_set( ImageValue *image, Imageinfo *ii ) { image_value_destroy( image ); image->ii = ii; if( ii ) { MANAGED_REF( image->ii ); image->file_changed_sid = g_signal_connect( G_OBJECT( image->ii ), "file_changed", G_CALLBACK( image_value_file_changed_cb ), image ); } #ifdef DEBUG printf( "iimage_instance_update: ii = %p\n", ii ); #endif /*DEBUG*/ } /* Generate a descriptive name for an imagevalue. Used by plot.c etc. as well. */ void image_value_caption( ImageValue *value, VipsBuf *buf ) { Imageinfo *ii = value->ii; Classmodel *classmodel = value->classmodel; /* Show the filename if this ii came from a file, otherwise show * the class. */ if( ii && imageinfo_is_from_file( ii ) && classmodel->filename ) vips_buf_appends( buf, im_skip_dir( classmodel->filename ) ); else if( !heapmodel_name( HEAPMODEL( classmodel ), buf ) ) /* Only if there's no value, I think. */ vips_buf_appends( buf, CLASS_IMAGE ); } void * classmodel_get_instance( Classmodel *classmodel ) { ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); if( class && class->get_instance ) return( class->get_instance( classmodel ) ); return( NULL ); } static void classmodel_graphic_save_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Classmodel *classmodel = CLASSMODEL( client ); ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); char *filename; if( (filename = filesel_get_filename( filesel )) ) { if( class->graphic_save( classmodel, GTK_WIDGET( iwnd ), filename ) ) { IM_SETSTR( classmodel->filename, filename ); iobject_changed( IOBJECT( classmodel ) ); nfn( sys, IWINDOW_YES ); } else nfn( sys, IWINDOW_ERROR ); g_free( filename ); } else nfn( sys, IWINDOW_ERROR ); } void classmodel_graphic_save( Classmodel *classmodel, GtkWidget *parent ) { ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); GtkWidget *filesel; char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( !class->graphic_save ) { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() method not implemented for %s." ), "graphic_save", IOBJECT_GET_CLASS_NAME( classmodel ) ); iwindow_alert( parent, GTK_MESSAGE_ERROR ); return; } filesel = filesel_new(); row_qualified_name( HEAPMODEL( classmodel )->row, &buf ); iwindow_set_title( IWINDOW( filesel ), _( "Save %s \"%s\"" ), IOBJECT_GET_CLASS_NAME( classmodel ), vips_buf_all( &buf ) ); filesel_set_flags( FILESEL( filesel ), TRUE, TRUE ); filesel_set_filetype( FILESEL( filesel ), class->filetype, watch_int_get( main_watchgroup, class->filetype_pref, 0 ) ); filesel_set_filetype_pref( FILESEL( filesel ), class->filetype_pref ); iwindow_set_parent( IWINDOW( filesel ), parent ); idialog_set_iobject( IDIALOG( filesel ), IOBJECT( classmodel ) ); filesel_set_done( FILESEL( filesel ), classmodel_graphic_save_cb, classmodel ); iwindow_build( IWINDOW( filesel ) ); if( classmodel->filename ) filesel_set_filename( FILESEL( filesel ), classmodel->filename ); gtk_widget_show( GTK_WIDGET( filesel ) ); } static void classmodel_graphic_replace_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Classmodel *classmodel = CLASSMODEL( client ); ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); char *filename; if( (filename = filesel_get_filename( filesel )) ) { if( class->graphic_replace( classmodel, GTK_WIDGET( iwnd ), filename ) ) { /* Make sure client stays alive through the * recalculate. */ g_object_ref( G_OBJECT( classmodel ) ); symbol_recalculate_all(); IM_SETSTR( classmodel->filename, filename ); iobject_changed( IOBJECT( classmodel ) ); g_object_unref( G_OBJECT( classmodel ) ); nfn( sys, IWINDOW_YES ); } else nfn( sys, IWINDOW_ERROR ); g_free( filename ); } else nfn( sys, IWINDOW_ERROR ); } void classmodel_graphic_replace( Classmodel *classmodel, GtkWidget *parent ) { ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); GtkWidget *filesel; char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( !class->graphic_replace ) { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() method not implemented for %s." ), "graphic_replace", IOBJECT_GET_CLASS_NAME( classmodel ) ); iwindow_alert( parent, GTK_MESSAGE_ERROR ); return; } row_qualified_name( HEAPMODEL( classmodel )->row, &buf ); filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Replace %s \"%s\"" ), IOBJECT_GET_CLASS_NAME( classmodel ), vips_buf_all( &buf ) ); filesel_set_flags( FILESEL( filesel ), TRUE, FALSE ); filesel_set_filetype( FILESEL( filesel ), class->filetype, watch_int_get( main_watchgroup, class->filetype_pref, 0 ) ); filesel_set_filetype_pref( FILESEL( filesel ), class->filetype_pref ); iwindow_set_parent( IWINDOW( filesel ), parent ); idialog_set_iobject( IDIALOG( filesel ), IOBJECT( classmodel ) ); filesel_set_done( FILESEL( filesel ), classmodel_graphic_replace_cb, classmodel ); iwindow_build( IWINDOW( filesel ) ); if( classmodel->filename ) filesel_set_filename( FILESEL( filesel ), classmodel->filename ); gtk_widget_show( GTK_WIDGET( filesel ) ); } /* Make and break links between classmodels and the iimages displaying them. */ static void classmodel_iimage_link( Classmodel *classmodel, iImage *iimage ) { if( !g_slist_find( classmodel->iimages, iimage ) ) { #ifdef DEBUG printf( "classmodel_iimage_link: linking " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( " to " ); row_name_print( HEAPMODEL( iimage )->row ); printf( "\n" ); #endif /*DEBUG*/ iimage->classmodels = g_slist_prepend( iimage->classmodels, classmodel ); classmodel->iimages = g_slist_prepend( classmodel->iimages, iimage ); } } void * classmodel_iimage_unlink( Classmodel *classmodel, iImage *iimage ) { if( g_slist_find( classmodel->iimages, iimage ) ) { #ifdef DEBUG printf( "classmodel_iimage_unlink: unlinking " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( " from " ); row_name_print( HEAPMODEL( iimage )->row ); printf( "\n" ); #endif /*DEBUG*/ iimage->classmodels = g_slist_remove( iimage->classmodels, classmodel ); classmodel->iimages = g_slist_remove( classmodel->iimages, iimage ); } return( NULL ); } static void * classmodel_iimage_unlink_rev( iImage *iimage, Classmodel *classmodel ) { return( classmodel_iimage_unlink( classmodel, iimage ) ); } typedef struct { Classmodel *classmodel; Imageinfo *ii; } ClassmodelSearch; static void * classmodel_iimage_expr_model( Model *model, ClassmodelSearch *parms ) { /* Look for iimages which aren't super ... ie. if this is a class * derived from Image, display on the derived class, not on the * superclass. */ if( IS_IIMAGE( model ) && HEAPMODEL( model )->row->sym && !is_super( HEAPMODEL( model )->row->sym ) && !is_this( HEAPMODEL( model )->row->sym ) ) { iImage *iimage = IIMAGE( model ); if( iimage->value.ii == parms->ii ) classmodel_iimage_link( parms->classmodel, iimage ); } return( NULL ); } /* This classmodel is defined on an Imageinfo recorded as having been the value * of expr ... find an associated iImage, and link to that. */ static void * classmodel_iimage_expr( Expr *expr, ClassmodelSearch *parms ) { if( expr->row ) { #ifdef DEBUG printf( "classmodel_iimage_expr: starting for " ); row_name_print( expr->row ); printf( "\n" ); #endif /*DEBUG*/ /* Search this part of the tally for an iImage with ii as its * derived value, and link to us. */ (void) icontainer_map_all( ICONTAINER( expr->row->top_row ), (icontainer_map_fn) classmodel_iimage_expr_model, parms ); } return( NULL ); } /* classmodel is defined on ii ... update all the classmodel->iimage links. */ void classmodel_iimage_update( Classmodel *classmodel, Imageinfo *ii ) { ClassmodelSearch parms; parms.classmodel = classmodel; parms.ii = ii; slist_map( classmodel->iimages, (SListMapFn) classmodel_iimage_unlink_rev, classmodel ); /* Don't make links for supers/this. */ if( HEAPMODEL( classmodel )->row->sym && !is_super( HEAPMODEL( classmodel )->row->sym ) && !is_this( HEAPMODEL( classmodel )->row->sym ) ) { #ifdef DEBUG printf( "classmodel_iimage_update: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( " is defined on ii \"%s\" ... searching for client " "displays\n", ii->im->filename ); #endif /*DEBUG*/ slist_map( imageinfo_expr_which( ii ), (SListMapFn) classmodel_iimage_expr, &parms ); } } static gboolean classmodel_class_member_new( Classmodel *classmodel, ClassmodelMember *m, Heap *heap, PElement *out ); static gboolean classmodel_dict_new( Classmodel *classmodel, ClassmodelMember *options, int noptions, Heap *heap, PElement *out ) { PElement list = *out; int i; /* Make first RHS ... the end of the list. */ heap_list_init( &list ); for( i = 0; i < noptions; i++ ) { PElement pair, key, value; if( !heap_list_add( heap, &list, &pair ) || !heap_list_add( heap, &pair, &key ) || !heap_list_add( heap, &pair, &value ) || !heap_managedstring_new( heap, options[i].member_name, &key ) || !classmodel_class_member_new( classmodel, &options[i], heap, &value ) ) return( FALSE ); (void) heap_list_next( &list ); } return( TRUE ); } static gboolean classmodel_class_member_new( Classmodel *classmodel, ClassmodelMember *m, Heap *heap, PElement *out ) { switch( m->type ) { case CLASSMODEL_MEMBER_INT: case CLASSMODEL_MEMBER_ENUM: if( !heap_real_new( heap, G_STRUCT_MEMBER( int, classmodel, m->offset ), out ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_BOOLEAN: if( !heap_bool_new( heap, G_STRUCT_MEMBER( gboolean, classmodel, m->offset ), out ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_DOUBLE: if( !heap_real_new( heap, G_STRUCT_MEMBER( double, classmodel, m->offset ), out ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_STRING: if( !heap_managedstring_new( heap, G_STRUCT_MEMBER( char *, classmodel, m->offset ), out ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_STRING_LIST: if( !heap_lstring_new( heap, G_STRUCT_MEMBER( GSList *, classmodel, m->offset ), out ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_REALVEC_FIXED: if( !heap_realvec_new( heap, m->extent, &G_STRUCT_MEMBER( double, classmodel, m->offset ), out ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_MATRIX: { MatrixValue *value = &G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset ); if( !heap_matrix_new( heap, value->width, value->height, value->coeff, out ) ) return( FALSE ); break; } case CLASSMODEL_MEMBER_OPTIONS: if( !classmodel_dict_new( classmodel, (ClassmodelMember *) m->details, m->extent, heap, out ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_IMAGE: { ImageValue *value = &G_STRUCT_MEMBER( ImageValue, classmodel, m->offset ); PEPUTP( out, ELEMENT_MANAGED, value->ii ); break; } default: g_assert( 0 ); } return( TRUE ); } /* Trigger the class_new method for a classmodel ... look for a constructor: * try CLASS_edit, then if that's not defined, try CLASS. Eg. * A1.Scale_edit from to value * if Scale_edit is not defined, try * A1.Scale from to value */ static gboolean classmodel_class_instance_new( Classmodel *classmodel ) { ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); Row *row = HEAPMODEL( classmodel )->row; PElement *root = &row->expr->root; const char *cname = IOBJECT( classmodel )->name; Reduce *rc = reduce_context; Heap *heap = rc->heap; char cname_new[256]; PElement fn; #ifdef DEBUG printf( "classmodel_class_instance_new: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ /* Find and build. */ im_snprintf( cname_new, 256, "%s_edit", cname ); if( !class_get_member( root, cname_new, NULL, &fn ) ) { if( !class_get_member( root, cname, NULL, &fn ) ) return( FALSE ); } if( class->class_new ) { if( !class->class_new( classmodel, &fn, root ) ) return( FALSE ); } else { int i; PElement rhs; heap_appl_init( root, &fn ); for( i = 0; i < class->n_members; i++ ) { if( !heap_appl_add( heap, root, &rhs ) ) return( FALSE ); if( !classmodel_class_member_new( classmodel, &class->members[i], heap, &rhs ) ) return( FALSE ); } } /* Reduce to base type. */ if( !reduce_pelement( rc, reduce_spine, root ) ) return( FALSE ); /* We have a new heap struct ... tell everyone to get new pointers. */ if( heapmodel_new_heap( HEAPMODEL( row ), root ) ) return( FALSE ); return( TRUE ); } static void classmodel_dispose( GObject *gobject ) { Classmodel *classmodel; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_CLASSMODEL( gobject ) ); classmodel = CLASSMODEL( gobject ); /* My instance destroy stuff. */ slist_map( classmodel->iimages, (SListMapFn) classmodel_iimage_unlink_rev, classmodel ); IM_FREE( classmodel->filename ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } /* We don't want subclases like Group to have an _info() method, since it * will appear in tooltips and the Container _info() is rather annoying. * * Things like iImage define an _info() with useful stuff in. */ static void classmodel_info( iObject *iobject, VipsBuf *buf ) { } static void classmodel_parent_add( iContainer *child ) { g_assert( IS_CLASSMODEL( child ) ); ICONTAINER_CLASS( parent_class )->parent_add( child ); } /* How many widgets we allow for member automation edit. */ #define MAX_WIDGETS (10) /* Widgets for classmodel edit. */ typedef struct _ClassmodelEdit { iDialog *idlg; Classmodel *classmodel; GtkWidget *widgets[MAX_WIDGETS]; } ClassmodelEdit; static gboolean classmodel_done_member( Classmodel *classmodel, ClassmodelMember *m, GtkWidget *widget ) { char txt[256]; switch( m->type ) { case CLASSMODEL_MEMBER_INT: case CLASSMODEL_MEMBER_ENUM: break; case CLASSMODEL_MEMBER_BOOLEAN: G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) = GTK_TOGGLE_BUTTON( widget )->active; break; case CLASSMODEL_MEMBER_DOUBLE: if( !get_geditable_double( widget, &G_STRUCT_MEMBER( double, classmodel, m->offset ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_STRING: get_geditable_string( widget, txt, 256 ); IM_SETSTR( G_STRUCT_MEMBER( char *, classmodel, m->offset ), txt ); break; case CLASSMODEL_MEMBER_STRING_LIST: case CLASSMODEL_MEMBER_REALVEC_FIXED: case CLASSMODEL_MEMBER_MATRIX: case CLASSMODEL_MEMBER_OPTIONS: case CLASSMODEL_MEMBER_IMAGE: break; default: g_assert( 0 ); } return( TRUE ); } /* Done button hit. */ static void classmodel_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { ClassmodelEdit *eds = (ClassmodelEdit *) client; Classmodel *classmodel = eds->classmodel; ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); int i; for( i = 0; i < class->n_members; i++ ) if( !classmodel_done_member( classmodel, &class->members[i], eds->widgets[i] ) ) { nfn( sys, IWINDOW_ERROR ); return; } /* Rebuild object. */ classmodel_update( classmodel ); symbol_recalculate_all(); nfn( sys, IWINDOW_YES ); } static GtkWidget * classmodel_buildedit_member( Classmodel *classmodel, ClassmodelMember *m, iDialog *idlg, GtkWidget *vb, GtkSizeGroup *group ) { GtkWidget *widget; widget = NULL; switch( m->type ) { case CLASSMODEL_MEMBER_INT: case CLASSMODEL_MEMBER_ENUM: break; case CLASSMODEL_MEMBER_BOOLEAN: widget = build_gtoggle( vb, _( m->user_name ) ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ), G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) ); set_tooltip( widget, _( "Set boolean value here" ) ); break; case CLASSMODEL_MEMBER_DOUBLE: widget = build_glabeltext4( vb, group, _( m->user_name ) ); idialog_init_entry( idlg, widget, _( "Enter a floating point number here" ), "%g", G_STRUCT_MEMBER( double, classmodel, m->offset ) ); break; case CLASSMODEL_MEMBER_STRING: widget = build_glabeltext4( vb, group, _( m->user_name ) ); idialog_init_entry( idlg, widget, _( "Enter a string here" ), "%s", G_STRUCT_MEMBER( char *, classmodel, m->offset ) ); break; case CLASSMODEL_MEMBER_STRING_LIST: case CLASSMODEL_MEMBER_REALVEC_FIXED: case CLASSMODEL_MEMBER_MATRIX: case CLASSMODEL_MEMBER_OPTIONS: case CLASSMODEL_MEMBER_IMAGE: break; default: g_assert( 0 ); } return( widget ); } /* Build the insides of edit. */ static void classmodel_buildedit( iDialog *idlg, GtkWidget *vb, ClassmodelEdit *eds ) { Classmodel *classmodel = eds->classmodel; ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); GtkSizeGroup *group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL ); int i; for( i = 0; i < class->n_members; i++ ) eds->widgets[i] = classmodel_buildedit_member( classmodel, &class->members[i], idlg, vb, group ); gtk_widget_show_all( vb ); g_object_unref( group ); } static void classmodel_edit( GtkWidget *parent, Model *model ) { Classmodel *classmodel = CLASSMODEL( model ); ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); if( class->n_members ) { GtkWidget *idlg; ClassmodelEdit *eds = INEW( NULL, ClassmodelEdit ); eds->classmodel = classmodel; idlg = idialog_new(); /* Expands to eg. "Edit Toggle A1". */ iwindow_set_title( IWINDOW( idlg ), _( "Edit %s %s" ), IOBJECT_GET_CLASS_NAME( model ), IOBJECT( HEAPMODEL( model )->row )->name ); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) classmodel_buildedit, eds, NULL, NULL ); idialog_set_callbacks( IDIALOG( idlg ), iwindow_true_cb, NULL, idialog_free_client, eds ); /* Expands to eg. "Set Toggle". */ idialog_add_ok( IDIALOG( idlg ), classmodel_done_cb, _( "Set %s" ), IOBJECT_GET_CLASS_NAME( classmodel ) ); iwindow_set_parent( IWINDOW( idlg ), parent ); idialog_set_iobject( IDIALOG( idlg ), IOBJECT( classmodel ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } } static gboolean classmodel_save_member( Classmodel *classmodel, ClassmodelMember *m, xmlNode *xthis ) { int i; switch( m->type ) { case CLASSMODEL_MEMBER_INT: case CLASSMODEL_MEMBER_ENUM: if( !set_iprop( xthis, m->save_name, G_STRUCT_MEMBER( int, classmodel, m->offset ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_BOOLEAN: if( !set_sprop( xthis, m->save_name, bool_to_char( G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_DOUBLE: if( !set_dprop( xthis, m->save_name, G_STRUCT_MEMBER( double, classmodel, m->offset ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_STRING: if( !set_sprop( xthis, m->save_name, G_STRUCT_MEMBER( char *, classmodel, m->offset ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_STRING_LIST: if( !set_slprop( xthis, m->save_name, G_STRUCT_MEMBER( GSList *, classmodel, m->offset ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_REALVEC_FIXED: for( i = 0; i < m->extent; i++ ) { char buf[256]; im_snprintf( buf, 256, "%s%d", m->save_name, i ); if( !set_dprop( xthis, buf, (&G_STRUCT_MEMBER( double, classmodel, m->offset ))[i] ) ) return( FALSE ); } break; case CLASSMODEL_MEMBER_MATRIX: { MatrixValue *value = &G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset ); const int n = value->width * value->height; if( !set_dlprop( xthis, "value", value->coeff, n ) || !set_iprop( xthis, "width", value->width ) || !set_iprop( xthis, "height", value->height ) ) return( FALSE ); break; } case CLASSMODEL_MEMBER_OPTIONS: for( i = 0; i < m->extent; i++ ) { ClassmodelMember *options = (ClassmodelMember *) m->details; if( !classmodel_save_member( classmodel, &options[i], xthis ) ) return( FALSE ); } break; case CLASSMODEL_MEMBER_IMAGE: break; default: g_assert( 0 ); } return( TRUE ); } static xmlNode * classmodel_save( Model *model, xmlNode *xnode ) { Classmodel *classmodel = CLASSMODEL( model ); ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); xmlNode *xthis; int i; #ifdef DEBUG printf( "classmodel_save: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( classmodel->edited ) for( i = 0; i < class->n_members; i++ ) if( !classmodel_save_member( classmodel, &class->members[i], xthis ) ) return( NULL ); return( xthis ); } static gboolean classmodel_load_member( Classmodel *classmodel, ClassmodelMember *m, xmlNode *xthis ) { char buf[MAX_STRSIZE]; gboolean found; int i; found = FALSE; switch( m->type ) { case CLASSMODEL_MEMBER_INT: if( get_iprop( xthis, m->save_name, &G_STRUCT_MEMBER( int, classmodel, m->offset ) ) ) found = TRUE; break; case CLASSMODEL_MEMBER_ENUM: { int v; if( get_iprop( xthis, m->save_name, &v ) ) { v = IM_CLIP( 0, v, m->extent ); G_STRUCT_MEMBER( int, classmodel, m->offset ) = v; found = TRUE; } break; } case CLASSMODEL_MEMBER_BOOLEAN: if( get_bprop( xthis, m->save_name, &G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) ) ) found = TRUE; break; case CLASSMODEL_MEMBER_DOUBLE: if( get_dprop( xthis, m->save_name, &G_STRUCT_MEMBER( double, classmodel, m->offset ) ) ) found = TRUE; break; case CLASSMODEL_MEMBER_STRING: if( get_sprop( xthis, m->save_name, buf, MAX_STRSIZE ) ) { IM_SETSTR( G_STRUCT_MEMBER( char *, classmodel, m->offset ), buf ); found = TRUE; } /* Nasty: before member automation, we used to always * save/load caption, as a member of model. Now caption is * only present if the class has it as a automated member. * Plus some classes used to not support captions (eg. Scale). * So: caption can be missing, even if it should be there. Set * a fall-back value. */ if( !found && strcmp( m->save_name, "caption" ) == 0 ) { IM_SETSTR( G_STRUCT_MEMBER( char *, classmodel, m->offset ), "" ); found = TRUE; } break; case CLASSMODEL_MEMBER_STRING_LIST: { GSList *slist; GSList **member = &G_STRUCT_MEMBER( GSList *, classmodel, m->offset ); if( get_slprop( xthis, m->member_name, &slist ) ) { IM_FREEF( slist_free_all, *member ); *member = slist; found = TRUE; } break; } case CLASSMODEL_MEMBER_REALVEC_FIXED: for( i = 0; i < m->extent; i++ ) { im_snprintf( buf, MAX_STRSIZE, "%s%d", m->save_name, i ); if( get_dprop( xthis, buf, &((&G_STRUCT_MEMBER( double, classmodel, m->offset ))[i]) ) ) found = TRUE; } break; case CLASSMODEL_MEMBER_MATRIX: { MatrixValue *value = &G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset ); if( get_dlprop( xthis, "value", &value->coeff ) && get_iprop( xthis, "width", &value->width ) && get_iprop( xthis, "height", &value->height ) ) found = TRUE; break; } case CLASSMODEL_MEMBER_OPTIONS: for( i = 0; i < m->extent; i++ ) { ClassmodelMember *options = (ClassmodelMember *) m->details; if( !classmodel_load_member( classmodel, &options[i], xthis ) ) return( FALSE ); } break; case CLASSMODEL_MEMBER_IMAGE: break; default: g_assert( 0 ); } return( found ); } static gboolean classmodel_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xthis ) { Classmodel *classmodel = CLASSMODEL( model ); ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); #ifdef DEBUG printf( "classmodel_load: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ /* Only for classes with member automation. */ if( class->n_members ) { gboolean all_found; int i; /* Before we mark the graphic as edited, insist all * members have values set. This can be important in * compatibility mode, where the old nip might not have * supported all the members we have. */ all_found = TRUE; for( i = 0; i < class->n_members; i++ ) all_found &= classmodel_load_member( classmodel, &class->members[i], xthis ); if( all_found ) classmodel_set_edited( CLASSMODEL( model ), TRUE ); } return( MODEL_CLASS( parent_class )->load( model, state, parent, xthis ) ); } static gboolean classmodel_get_item( Classmodel *classmodel, ClassmodelMember *m, PElement *value ); static void * classmodel_parse_option( const char *key, PElement *value, Classmodel *classmodel, ClassmodelMember *m ) { ClassmodelMember *options = (ClassmodelMember *) m->details; int noptions = m->extent; int i; for( i = 0; i < noptions; i++ ) if( strcmp( key, options[i].member_name ) == 0 ) break; if( i == noptions ) { error_top( _( "Unknown option." ) ); error_sub( _( "Option \"%s\" not known." ), key ); return( value ); } if( !classmodel_get_item( classmodel, &options[i], value ) ) return( value ); return( NULL ); } static gboolean classmodel_get_item( Classmodel *classmodel, ClassmodelMember *m, PElement *value ) { char buf[MAX_STRSIZE]; double vec[3]; int l; int i; double d; switch( m->type ) { case CLASSMODEL_MEMBER_INT: if( !heap_get_real( value, &d ) ) return( FALSE ); G_STRUCT_MEMBER( int, classmodel, m->offset ) = d; break; case CLASSMODEL_MEMBER_ENUM: if( !heap_get_real( value, &d ) ) return( FALSE ); d = IM_CLIP( 0, d, m->extent ); G_STRUCT_MEMBER( int, classmodel, m->offset ) = d; break; case CLASSMODEL_MEMBER_BOOLEAN: if( !heap_get_bool( value, &G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_DOUBLE: if( !heap_get_real( value, &G_STRUCT_MEMBER( double, classmodel, m->offset ) ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_STRING: if( !heap_get_string( value, buf, MAX_STRSIZE ) ) return( FALSE ); IM_SETSTR( G_STRUCT_MEMBER( char *, classmodel, m->offset ), buf ); break; case CLASSMODEL_MEMBER_STRING_LIST: { GSList *slist; GSList **member = &G_STRUCT_MEMBER( GSList *, classmodel, m->offset ); if( !heap_get_lstring( value, &slist ) ) return( FALSE ); IM_FREEF( slist_free_all, *member ); *member = slist; break; } case CLASSMODEL_MEMBER_REALVEC_FIXED: g_assert( m->extent < 4 ); if( (l = heap_get_realvec( value, vec, m->extent )) < 0 ) return( FALSE ); if( l != m->extent ) { error_top( _( "Bad value." ) ); error_sub( _( "%d band value only" ), m->extent ); return( FALSE ); } for( i = 0; i < m->extent; i++ ) (&G_STRUCT_MEMBER( double, classmodel, m->offset ))[i] = vec[i]; break; case CLASSMODEL_MEMBER_MATRIX: { MatrixValue *matrix = &G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset ); int w, h; if( !heap_get_matrix_size( value, &w, &h ) || !matrix_value_resize( matrix, w, h ) || !heap_get_matrix( value, matrix->coeff, matrix->width * matrix->height, &w, &h ) ) return( FALSE ); break; } case CLASSMODEL_MEMBER_OPTIONS: /* If there are optional fields, we have to have a reset * method for clearing the ones we don't use. */ g_assert( CLASSMODEL_GET_CLASS( classmodel )->reset ); if( heap_map_dict( value, (heap_map_dict_fn) classmodel_parse_option, classmodel, m ) ) return( FALSE ); break; case CLASSMODEL_MEMBER_IMAGE: { ImageValue *image = &G_STRUCT_MEMBER( ImageValue, classmodel, m->offset ); Imageinfo *ii; g_assert( image->classmodel == classmodel ); if( !heap_get_image( value, &ii ) ) return( FALSE ); image_value_set( image, ii ); break; } default: g_assert( 0 ); } return( TRUE ); } static gboolean classmodel_update_model_member( Classmodel *classmodel, ClassmodelMember *m, PElement *root ) { PElement value; if( !class_get_member( root, m->member_name, NULL, &value ) ) return( FALSE ); #ifdef DEBUG printf( "classmodel_update_model_member: setting %s = ", m->member_name ); pgraph( &value ); #endif /*DEBUG*/ if( !classmodel_get_item( classmodel, m, &value ) ) return( FALSE ); return( TRUE ); } /* Update all members from the heap. Also used from graph_export_image. */ gboolean classmodel_update_members( Classmodel *classmodel, PElement *root ) { ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); int i; for( i = 0; i < class->n_members; i++ ) if( !classmodel_update_model_member( classmodel, &class->members[i], root ) ) return( FALSE ); if( class->class_get && !class->class_get( classmodel, root ) ) return( FALSE ); return( TRUE ); } static void * classmodel_update_model( Heapmodel *heapmodel ) { Classmodel *classmodel = CLASSMODEL( heapmodel ); ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel ); #ifdef DEBUG printf( "classmodel_update_model: " ); row_name_print( heapmodel->row ); printf( "\n" ); #endif /*DEBUG*/ /* If necessary, reset model to default. */ if( class->reset ) class->reset( classmodel ); if( heapmodel->row && heapmodel->row->expr ) { Expr *expr = heapmodel->row->expr; if( !heapmodel->modified ) if( !classmodel_update_members( classmodel, &expr->root ) ) return( classmodel ); } return( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ); } static void * classmodel_update_heap( Heapmodel *heapmodel ) { Classmodel *classmodel = CLASSMODEL( heapmodel ); #ifdef DEBUG printf( "classmodel_update_heap: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ /* Nasty: classmodel_class_instance_new() can (indirectly) destroy us. * Wrap a _ref()/_unref() pair around it to make sure we stay alive. */ g_object_ref( G_OBJECT( heapmodel ) ); /* Build a new instance from the model. */ if( !classmodel_class_instance_new( classmodel ) ) { g_object_unref( G_OBJECT( heapmodel ) ); return( heapmodel ); } if( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) ) { g_object_unref( G_OBJECT( heapmodel ) ); return( heapmodel ); } g_object_unref( G_OBJECT( heapmodel ) ); return( NULL ); } static void * classmodel_clear_edited( Heapmodel *heapmodel ) { Classmodel *classmodel = CLASSMODEL( heapmodel ); classmodel_set_edited( classmodel, FALSE ); return( HEAPMODEL_CLASS( parent_class )->clear_edited( heapmodel ) ); } static gboolean classmodel_real_class_get( Classmodel *classmodel, PElement *root ) { return( TRUE ); } static void classmodel_class_init( ClassmodelClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Init methods. */ gobject_class->dispose = classmodel_dispose; iobject_class->info = classmodel_info; icontainer_class->parent_add = classmodel_parent_add; model_class->edit = classmodel_edit; model_class->save = classmodel_save; model_class->load = classmodel_load; heapmodel_class->update_model = classmodel_update_model; heapmodel_class->update_heap = classmodel_update_heap; heapmodel_class->clear_edited = classmodel_clear_edited; classmodel_class->get_instance = NULL; classmodel_class->class_get = classmodel_real_class_get; classmodel_class->class_new = NULL; classmodel_class->graphic_save = NULL; classmodel_class->graphic_replace = NULL; classmodel_class->filetype = filesel_type_any; classmodel_class->filetype_pref = NULL; classmodel_class->members = NULL; classmodel_class->n_members = 0; } static void classmodel_init( Classmodel *classmodel ) { Model *model = MODEL( classmodel ); model->display = FALSE; classmodel->edited = FALSE; classmodel->iimages = NULL; classmodel->views = NULL; classmodel->filename = NULL; } GType classmodel_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ClassmodelClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) classmodel_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Classmodel ), 32, /* n_preallocs */ (GInstanceInitFunc) classmodel_init, }; type = g_type_register_static( TYPE_HEAPMODEL, "Classmodel", &info, 0 ); } return( type ); } void classmodel_set_edited( Classmodel *classmodel, gboolean edited ) { if( classmodel->edited != edited ) { #ifdef DEBUG printf( "classmodel_set_edited: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( " %s\n", bool_to_char( edited ) ); #endif /*DEBUG*/ classmodel->edited = edited; iobject_changed( IOBJECT( classmodel ) ); if( HEAPMODEL( classmodel )->row && HEAPMODEL( classmodel )->row->expr ) expr_dirty( HEAPMODEL( classmodel )->row->expr, link_serial_new() ); } /* Mark eds for application. */ if( edited ) heapmodel_set_modified( HEAPMODEL( classmodel ), TRUE ); } /* The model has changed: mark for recomp. */ void classmodel_update( Classmodel *classmodel ) { Row *row = HEAPMODEL( classmodel )->row; /* Eg. for no symol on load. */ if( !row->expr ) return; #ifdef DEBUG printf( "classmodel_update: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ /* classmodel_update_heap() will rebuild us on recomp. */ classmodel_set_edited( classmodel, TRUE ); expr_dirty( row->expr, link_serial_new() ); workspace_set_modified( row->ws, TRUE ); } /* Make a new classmodel subtype (eg. TYPE_PATHNAME) and link it on. */ Classmodel * classmodel_new_classmodel( GType type, Rhs *rhs ) { Classmodel *classmodel; classmodel = g_object_new( type, NULL ); icontainer_child_add( ICONTAINER( rhs ), ICONTAINER( classmodel ), -1 ); return( classmodel ); } ================================================ FILE: src/classmodel.h ================================================ /* like a heapmodel, but we represent a class in the heap */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Member types we automate. */ typedef enum { CLASSMODEL_MEMBER_INT, CLASSMODEL_MEMBER_ENUM, /* Like int, but extent has max value */ CLASSMODEL_MEMBER_BOOLEAN, CLASSMODEL_MEMBER_DOUBLE, CLASSMODEL_MEMBER_STRING, CLASSMODEL_MEMBER_STRING_LIST, CLASSMODEL_MEMBER_REALVEC_FIXED,/* Eg. Colour's triplet */ CLASSMODEL_MEMBER_MATRIX, CLASSMODEL_MEMBER_OPTIONS, CLASSMODEL_MEMBER_IMAGE } ClassmodelMemberType; /* A matrix value. */ typedef struct _MatrixValue { double *coeff; /* Base coeffs */ int width; /* Size of matrix */ int height; } MatrixValue; /* An image value. */ typedef struct { Imageinfo *ii; /* Can get "changed" for reload if the file changes behind our backs. * Recalc the classmodel if this happens. */ guint file_changed_sid; Classmodel *classmodel; } ImageValue; /* A member needing automation. */ typedef struct { ClassmodelMemberType type; void *details; /* eg. the set of allowed options */ int extent; /* Vector length, enum max, etc. */ const char *member_name; /* Name as known in nip class defs */ const char *save_name; /* As known in save files */ const char *user_name; /* i18n'd name for dialogs */ guint offset; /* Struct offset */ } ClassmodelMember; #define TYPE_CLASSMODEL (classmodel_get_type()) #define CLASSMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_CLASSMODEL, Classmodel )) #define CLASSMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_CLASSMODEL, ClassmodelClass)) #define IS_CLASSMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_CLASSMODEL )) #define IS_CLASSMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_CLASSMODEL )) #define CLASSMODEL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_CLASSMODEL, ClassmodelClass )) struct _Classmodel { Heapmodel parent_class; /* Set if we have graphic mods applied which should be saved. */ gboolean edited; /* xtras for Region/Arrow/etc. */ GSList *iimages; /* All the iimage we are defined on */ GSList *views; /* All the regionview we have made */ /* For things which have been loaded or saved from files (eg. image * and matrix). Used to set the filename for the "save" dialog. */ char *filename; }; typedef struct _ClassmodelClass { HeapmodelClass parent_class; /* Get a pointer to the class instance vars ... just used by * iarrow/iregion for code sharing. */ void *(*get_instance)( Classmodel * ); /* Read from heap into model, and create new heap class from model. */ gboolean (*class_get)( Classmodel *, PElement *root ); gboolean (*class_new)( Classmodel *, PElement *fn, PElement *out ); /* Save and replace graphic displays ... eg. image/matrix. */ gboolean (*graphic_save)( Classmodel *, GtkWidget *, const char * ); gboolean (*graphic_replace)( Classmodel *, GtkWidget *, const char * ); FileselFileType **filetype; const char *filetype_pref; ClassmodelMember *members; int n_members; void (*reset)( Classmodel * ); } ClassmodelClass; void image_value_init( ImageValue *image, Classmodel *classmodel ); void image_value_destroy( ImageValue *image ); void image_value_set( ImageValue *image, Imageinfo *ii ); void image_value_caption( ImageValue *value, VipsBuf *buf ); void *classmodel_get_instance( Classmodel *classmodel ); void classmodel_graphic_save( Classmodel *classmodel, GtkWidget *parent ); void classmodel_graphic_replace( Classmodel *classmodel, GtkWidget *parent ); void *classmodel_iimage_unlink( Classmodel *classmodel, iImage *iimage ); void classmodel_iimage_update( Classmodel *classmodel, Imageinfo *ii ); gboolean classmodel_update_members( Classmodel *classmodel, PElement *root ); GType classmodel_get_type( void ); void classmodel_update( Classmodel *classmodel ); void classmodel_set_edited( Classmodel *classmodel, gboolean edited ); Classmodel *classmodel_new_classmodel( GType type, Rhs *rhs ); ================================================ FILE: src/clock.c ================================================ /* a clock ... triggers a recomp every whenever */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ValueClass *parent_class = NULL; static void clock_dispose( GObject *gobject ) { Clock *clock = CLOCK( gobject ); #ifdef DEBUG printf( "clock_dispose\n" ); #endif /*DEBUG*/ IM_FREEF( g_source_remove, clock->recalc_timeout ); IM_FREEF( g_timer_destroy, clock->elapsed_timer ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void clock_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Clock *clock = CLOCK( client ); Stringset *ss = STRINGSET( iwnd ); StringsetChild *interval = stringset_child_get( ss, _( "Interval" ) ); StringsetChild *value = stringset_child_get( ss, _( "Elapsed time" ) ); if( !get_geditable_double( interval->entry, &clock->interval ) || !get_geditable_double( value->entry, &clock->value ) ) { nfn( sys, IWINDOW_ERROR ); return; } if( clock->interval < 0.1 ) clock->interval = 0.1; if( clock->value < 0.0 ) clock->value = 0.0; /* Magic: ask for the clock timer to be reset. */ clock->time_offset = -1; classmodel_update( CLASSMODEL( clock ) ); symbol_recalculate_all(); nfn( sys, IWINDOW_YES ); } static void clock_edit( GtkWidget *parent, Model *model ) { Clock *clock = CLOCK( model ); GtkWidget *ss = stringset_new(); char txt[256]; im_snprintf( txt, 256, "%g", clock->interval ); stringset_child_new( STRINGSET( ss ), _( "Interval" ), txt, _( "Interval between ticks (seconds)" ) ); im_snprintf( txt, 256, "%g", clock->value ); stringset_child_new( STRINGSET( ss ), _( "Elapsed time" ), txt, _( "Elapsed time (seconds)" ) ); /* Expands to eg. "Edit Toggle A1". */ iwindow_set_title( IWINDOW( ss ), _( "Edit %s %s" ), IOBJECT_GET_CLASS_NAME( model ), IOBJECT( HEAPMODEL( model )->row )->name ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, clock ); idialog_add_ok( IDIALOG( ss ), clock_done_cb, _( "Set %s" ), IOBJECT_GET_CLASS_NAME( model ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( parent ) ); idialog_set_iobject( IDIALOG( ss ), IOBJECT( model ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } static gboolean clock_timeout_cb( Clock *clock ) { #ifdef DEBUG printf( "clock_timeout_cb: " ); row_name_print( HEAPMODEL( clock )->row ); printf( " interval=%g, value=%g\n", clock->interval, clock->value ); #endif /*DEBUG*/ /* Test autocalc ... if it's off, make sure we don't update the * interface. */ if( mainw_auto_recalc ) { clock->value = g_timer_elapsed( clock->elapsed_timer, NULL ) + clock->time_offset; classmodel_update( CLASSMODEL( clock ) ); symbol_recalculate_all(); } return( TRUE ); } static void * clock_update_model( Heapmodel *heapmodel ) { Clock *clock = CLOCK( heapmodel ); #ifdef DEBUG printf( "clock_update_model: " ); row_name_print( HEAPMODEL( clock )->row ); printf( " interval=%g, value=%g\n", clock->interval, clock->value ); #endif /*DEBUG*/ if( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ) return( heapmodel ); /* Milliseconds for the update timeout ... don't let it go under 100, * there's a danger the interface will lock up. */ int ms = IM_MAX( 100, clock->interval * 1000 ); IM_FREEF( g_source_remove, clock->recalc_timeout ); clock->recalc_timeout = g_timeout_add( ms, (GSourceFunc) clock_timeout_cb, clock ); /* Should we reset the timer from the value? */ if( clock->time_offset == -1 ) { g_timer_start( clock->elapsed_timer ); clock->time_offset = clock->value; } return( NULL ); } /* Override value_generate_caption(): pick from the model. */ static const char * clock_generate_caption( iObject *iobject ) { Value *value = VALUE( iobject ); ValueClass *value_class = VALUE_GET_CLASS( value ); Clock *clock = CLOCK( iobject ); VipsBuf *buf = &value->caption_buffer; vips_buf_rewind( buf ); if( !heapmodel_name( HEAPMODEL( value ), buf ) ) vips_buf_appends( buf, G_OBJECT_CLASS_NAME( value_class ) ); vips_buf_appendf( buf, " %g %g", clock->interval, clock->value ); return( vips_buf_all( buf ) ); } /* Members of clock we automate. */ static ClassmodelMember clock_members[] = { { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_INTERVAL, "interval", N_( "Interval" ), G_STRUCT_OFFSET( Clock, interval ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Clock, value ) } }; static void clock_class_init( ClockClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = clock_dispose; iobject_class->user_name = _( "Clock" ); iobject_class->generate_caption = clock_generate_caption; model_class->edit = clock_edit; heapmodel_class->update_model = clock_update_model; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = clock_members; classmodel_class->n_members = IM_NUMBER( clock_members ); } static void clock_init( Clock *clock ) { #ifdef DEBUG printf( "clock_init\n" ); #endif /*DEBUG*/ /* Overridden later. Just something sensible. */ clock->interval = 1; clock->value = 0; /* time_offset: set to -1 means we should set the offset from value on * the next rebuild. */ clock->elapsed_timer = g_timer_new(); clock->time_offset = -1; clock->recalc_timeout = 0; iobject_set( IOBJECT( clock ), CLASS_CLOCK, NULL ); } GType clock_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ClockClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) clock_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Clock ), 32, /* n_preallocs */ (GInstanceInitFunc) clock_init, }; type = g_type_register_static( TYPE_VALUE, "Clock", &info, 0 ); } return( type ); } ================================================ FILE: src/clock.h ================================================ /* a clock in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_CLOCK (clock_get_type()) #define CLOCK( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_CLOCK, Clock )) #define CLOCK_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_CLOCK, ClockClass)) #define IS_CLOCK( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_CLOCK )) #define IS_CLOCK_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_CLOCK )) #define CLOCK_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_CLOCK, ClockClass )) typedef struct _Clock { Value parent_object; double interval; double value; GTimer *elapsed_timer; double time_offset; /* Offset timer by this to get new value */ guint recalc_timeout; /* Timeout for next recalc */ } Clock; typedef struct _ClockClass { ValueClass parent_class; /* My methods. */ } ClockClass; GType clock_get_type( void ); ================================================ FILE: src/colour.c ================================================ /* an image class object in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Set of allowed colour_space strings. Do a case-insensitive match. */ static const char *colour_colour_space[] = { "xyz", /* index 0 */ "yxy", /* index 1 */ "lab", /* index 2 */ "lch", /* index 3 */ "ucs", /* index 4 */ "rgb", /* index 5 */ "srgb", /* index 6 */ "rgb16", /* index 7 */ "grey16" /* index 8 */ }; /* For each allowed colourspace, the corresponding VIPS Type value. */ static const int colour_type[] = { IM_TYPE_XYZ, IM_TYPE_YXY, IM_TYPE_LAB, IM_TYPE_LCH, IM_TYPE_UCS, IM_TYPE_RGB, IM_TYPE_sRGB, IM_TYPE_RGB16, IM_TYPE_GREY16 }; static ClassmodelClass *parent_class = NULL; static void colour_finalize( GObject *gobject ) { Colour *colour = COLOUR( gobject ); IM_FREE( colour->colour_space ); vips_buf_destroy( &colour->caption ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Widgets for colour edit. */ typedef struct _ColourEdit { iDialog *idlg; Colour *colour; GtkWidget *colour_widget; } ColourEdit; /* Find the VIPS type for a colour space string. */ static int colour_get_vips_type( Colour *colour ) { int type; int i; /* Default to something harmless. */ type = IM_TYPE_MULTIBAND; if( colour->colour_space ) for( i = 0; i < IM_NUMBER( colour_colour_space ); i++ ) if( strcasecmp( colour->colour_space, colour_colour_space[i] ) == 0 ) { type = colour_type[i]; break; } return( type ); } /* Are two doubles more or less equal. We need this when we check * for update to stop loops. The 0.0001 is a bit of a fudge :-( */ #define DEQ( A, B ) (ABS((A) - (B)) < 0.0001) /* Update non-model stuff in object from the model params. */ static void colour_refresh( Colour *colour ) { vips_buf_rewind( &colour->caption ); vips_buf_appendf( &colour->caption, CLASS_COLOUR " %s [%g, %g, %g]", NN( colour->colour_space ), colour->value[0], colour->value[1], colour->value[2] ); } void colour_set_colour( Colour *colour, const char *colour_space, double value[3] ) { int i; /* No change? */ for( i = 0; i < 3; i++ ) if( !DEQ( value[i], colour->value[i] ) ) break; if( i == 3 && colour_space && strcmp( colour_space, colour->colour_space ) == 0 ) return; for( i = 0; i < 3; i++ ) colour->value[i] = value[i]; IM_SETSTR( colour->colour_space, colour_space ); colour_refresh( colour ); classmodel_update( CLASSMODEL( colour ) ); symbol_recalculate_all(); } /* Code up a colour as an ii. Refcount zero! Will go on next GC. */ Imageinfo * colour_ii_new( Colour *colour ) { Imageinfo *imageinfo; int i; if( !(imageinfo = imageinfo_new_temp( main_imageinfogroup, reduce_context->heap, NULL, "t" )) ) return( NULL ); /* Make a 3 band 32-bit FLOAT memory image. */ im_initdesc( imageinfo->im, 1, 1, 3, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, IM_CODING_NONE, colour_get_vips_type( colour ), 1.0, 1.0, 0, 0 ); if( im_setupout( imageinfo->im ) ) return( NULL ); for( i = 0; i < 3; i++ ) ((float *) imageinfo->im->data)[i] = colour->value[i]; return( imageinfo ); } /* Convert our colour to rgb. Slow! */ static void colour_get_rgb( Colour *colour, double rgb[4] ) { int i; Imageinfo *imageinfo; for( i = 0; i < 4; i++ ) rgb[i] = 0.0; if( (imageinfo = colour_ii_new( colour )) ) imageinfo_to_rgb( imageinfo, rgb ); } void colour_set_rgb( Colour *colour, double rgb[4] ) { Imageinfo *imageinfo; if( (imageinfo = colour_ii_new( colour )) ) { double old_rgb[4]; double value[3]; int i; /* Setting as RGB can't express small differences since we're * going via 8 bit RGB. So only accept the new value if it's * sufficiently different from * what we have now. */ colour_get_rgb( colour, old_rgb ); if( fabs( rgb[0] - old_rgb[0] ) > (0.5 / 255) || fabs( rgb[1] - old_rgb[1] ) > (0.5 / 255) || fabs( rgb[2] - old_rgb[2] ) > (0.5 / 255) ) { imageinfo_from_rgb( imageinfo, rgb ); for( i = 0; i < 3; i++ ) value[i] = ((float *) imageinfo->im->data)[i]; colour_set_colour( colour, colour->colour_space, value ); } } } /* Done button hit. */ static void colour_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { ColourEdit *eds = (ColourEdit *) client; Colour *colour = eds->colour; double rgb[4]; gtk_color_selection_get_color( GTK_COLOR_SELECTION( eds->colour_widget ), rgb ); colour_set_rgb( colour, rgb ); nfn( sys, IWINDOW_YES ); } /* Build the insides of colour edit. */ static void colour_buildedit( iDialog *idlg, GtkWidget *work, ColourEdit *eds ) { Colour *colour = eds->colour; double rgb[4]; eds->colour_widget = gtk_color_selection_new(); gtk_color_selection_set_has_opacity_control( GTK_COLOR_SELECTION( eds->colour_widget ), FALSE ); colour_get_rgb( colour, rgb ); gtk_color_selection_set_color( GTK_COLOR_SELECTION( eds->colour_widget ), rgb ); gtk_box_pack_start( GTK_BOX( work ), eds->colour_widget, TRUE, TRUE, 2 ); gtk_widget_show_all( work ); } static void colour_edit( GtkWidget *parent, Model *model ) { Colour *colour = COLOUR( model ); ColourEdit *eds = INEW( NULL, ColourEdit ); GtkWidget *idlg; eds->colour = colour; idlg = idialog_new(); iwindow_set_title( IWINDOW( idlg ), _( "Edit %s %s" ), IOBJECT_GET_CLASS_NAME( model ), IOBJECT( HEAPMODEL( model )->row )->name ); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) colour_buildedit, eds, NULL, NULL ); idialog_set_callbacks( IDIALOG( idlg ), iwindow_true_cb, NULL, idialog_free_client, eds ); idialog_add_ok( IDIALOG( idlg ), colour_done_cb, _( "Set %s" ), IOBJECT_GET_CLASS_NAME( model ) ); iwindow_set_parent( IWINDOW( idlg ), parent ); idialog_set_iobject( IDIALOG( idlg ), IOBJECT( model ) ); idialog_set_pinup( IDIALOG( idlg ), TRUE ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } static View * colour_view_new( Model *model, View *parent ) { return( colourview_new() ); } static void * colour_update_model( Heapmodel *heapmodel ) { Colour *colour = COLOUR( heapmodel ); if( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ) return( heapmodel ); colour_refresh( colour ); return( NULL ); } /* Members of colour we automate. */ static ClassmodelMember colour_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_COLOUR_SPACE, "colour_space", N_( "Color Space" ), G_STRUCT_OFFSET( Colour, colour_space ) }, { CLASSMODEL_MEMBER_REALVEC_FIXED, NULL, 3, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Colour, value ) } }; static void colour_class_init( ColourClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = colour_finalize; model_class->view_new = colour_view_new; model_class->edit = colour_edit; heapmodel_class->update_model = colour_update_model; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = colour_members; classmodel_class->n_members = IM_NUMBER( colour_members ); } static void colour_init( Colour *colour ) { colour->value[0] = 0.0; colour->value[1] = 0.0; colour->value[2] = 0.0; colour->colour_space = NULL; vips_buf_init_dynamic( &colour->caption, MAX_LINELENGTH ); iobject_set( IOBJECT( colour ), CLASS_COLOUR, NULL ); } GType colour_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ColourClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) colour_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Colour ), 32, /* n_preallocs */ (GInstanceInitFunc) colour_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Colour", &info, 0 ); } return( type ); } ================================================ FILE: src/colour.h ================================================ /* a colour colour in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_COLOUR (colour_get_type()) #define COLOUR( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_COLOUR, Colour )) #define COLOUR_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_COLOUR, ColourClass)) #define IS_COLOUR( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_COLOUR )) #define IS_COLOUR_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_COLOUR )) #define COLOUR_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_COLOUR, ColourClass )) struct _Colour { Classmodel parent_class; /* Class fields. */ double value[3]; char *colour_space; /* Build view caption here. */ VipsBuf caption; }; typedef struct _ColourClass { ClassmodelClass parent_class; /* My methods. */ } ColourClass; Imageinfo *colour_ii_new( Colour *colour ); void colour_set_rgb( Colour *colour, double rgb[3] ); GType colour_get_type( void ); ================================================ FILE: src/colourdisplay.c ================================================ /* run the display for an image in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Tag our drag-n-drops with these. */ enum { TARGET_COLOUR, TARGET_TEXT }; static ImagedisplayClass *parent_class = NULL; /* Prefer x-color drags for 3 band non-complex imageinfos, and for LABQ */ static void colourdisplay_set_drag_type( Colourdisplay *colourdisplay ) { static const GtkTargetEntry text_targets[] = { { "text/plain", 0, TARGET_TEXT }, { "application/x-color", 0, TARGET_COLOUR } }; static const GtkTargetEntry colour_targets[] = { { "application/x-color", 0, TARGET_COLOUR }, { "text/plain", 0, TARGET_TEXT } }; Imageinfo *imageinfo = IMAGEDISPLAY( colourdisplay )->conv->ii; IMAGE *im = imageinfo_get( FALSE, imageinfo ); const GtkTargetEntry *targets; if( !GTK_WIDGET_REALIZED( GTK_WIDGET( colourdisplay ) ) || !im ) return; if( im->Bands == 3 && !vips_bandfmt_iscomplex( im->BandFmt ) ) targets = colour_targets; else if( im->Coding == IM_CODING_LABQ ) targets = colour_targets; else targets = text_targets; gtk_drag_dest_unset( GTK_WIDGET( colourdisplay ) ); gtk_drag_dest_set( GTK_WIDGET( colourdisplay ), GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, targets, IM_NUMBER( text_targets ), GDK_ACTION_COPY ); gtk_drag_source_unset( GTK_WIDGET( colourdisplay ) ); gtk_drag_source_set( GTK_WIDGET( colourdisplay ), GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, targets, IM_NUMBER( text_targets ), GDK_ACTION_COPY | GDK_ACTION_MOVE ); } static void colourdisplay_realize( GtkWidget *widget ) { Colourdisplay *colourdisplay = COLOURDISPLAY( widget ); GTK_WIDGET_CLASS( parent_class )->realize( widget ); colourdisplay_set_drag_type( colourdisplay ); } static void colourdisplay_drag_begin( GtkWidget *widget, GdkDragContext *context ) { Colourdisplay *colourdisplay = COLOURDISPLAY( widget ); GtkWidget *window; double colours[4]; GdkColor bg; window = iimageview_drag_window_new( 48, 32 ); gtk_object_set_data_full( GTK_OBJECT( widget ), "nip2-drag-window", window, (GtkDestroyNotify) gtk_widget_destroy ); #ifdef DEBUG printf( "colourdisplay_drag_begin: generating drag swatch colour\n" ); #endif /*DEBUG*/ imageinfo_to_rgb( IMAGEDISPLAY( colourdisplay )->conv->ii, colours ); bg.red = 0xffff * colours[0]; bg.green = 0xffff * colours[1]; bg.blue = 0xffff * colours[2]; gtk_widget_modify_bg( window, GTK_STATE_NORMAL, &bg ); gtk_drag_set_icon_widget( context, window, -2, -2 ); } static void colourdisplay_drag_end( GtkWidget *widget, GdkDragContext *context ) { gtk_object_set_data( GTK_OBJECT( widget ), "nip2-drag-window", NULL ); } static void colourdisplay_drag_data_get( GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time ) { Colourdisplay *colourdisplay = COLOURDISPLAY( widget ); Imageinfo *imageinfo = IMAGEDISPLAY( colourdisplay )->conv->ii; double colours[3]; guint16 vals[4]; char vips_buf_text[256]; VipsBuf buf = VIPS_BUF_STATIC( vips_buf_text ); switch( info ) { case TARGET_COLOUR: imageinfo_to_rgb( imageinfo, colours ); vals[0] = IM_RINT( colours[0] * 0xffff ); vals[1] = IM_RINT( colours[1] * 0xffff ); vals[2] = IM_RINT( colours[2] * 0xffff ); vals[3] = 0xffff; gtk_selection_data_set( selection_data, gdk_atom_intern( "application/x-color", FALSE ), 16, (guchar *) vals, 8 ); #ifdef DEBUG printf( "colourdisplay_drag_data_get: sending x-color\n" ); #endif /*DEBUG*/ break; case TARGET_TEXT: imageinfo_to_text( imageinfo, &buf ); gtk_selection_data_set( selection_data, gdk_atom_intern( "text/plain", FALSE ), 8, (guchar *) vips_buf_all( &buf ), strlen( vips_buf_all( &buf ) ) ); #ifdef DEBUG printf( "colourdisplay_drag_data_get: sending text/plain\n" ); #endif /*DEBUG*/ break; default: g_assert( FALSE ); break; } } static void colourdisplay_drag_data_received( GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time ) { Colourdisplay *colourdisplay = COLOURDISPLAY( widget ); Imageinfo *imageinfo = IMAGEDISPLAY( colourdisplay )->conv->ii; guint16 *vals; gdouble old_rgb[4]; gdouble rgb[4]; if( selection_data->length < 0 ) return; switch( info ) { case TARGET_COLOUR: if( selection_data->format != 16 || selection_data->length != 8 ) return; #ifdef DEBUG printf( "colourdisplay_drag_data_received: seen x-color\n" ); #endif /*DEBUG*/ vals = (guint16 *)selection_data->data; rgb[0] = (double) vals[0] / 0xffff; rgb[1] = (double) vals[1] / 0xffff; rgb[2] = (double) vals[2] / 0xffff; /* Dragging as RGB can't express small differences. So only * accept the new value if it's sufficiently different from * what we have now. */ imageinfo_to_rgb( imageinfo, old_rgb ); if( fabs( rgb[0] - old_rgb[0] ) > (0.5 / 255) || fabs( rgb[1] - old_rgb[1] ) > (0.5 / 255) || fabs( rgb[2] - old_rgb[2] ) > (0.5 / 255) ) imageinfo_from_rgb( imageinfo, rgb ); break; case TARGET_TEXT: if( selection_data->format != 8 ) return; #ifdef DEBUG printf( "colourdisplay_drag_data_received: seen text/plain\n" ); #endif /*DEBUG*/ if( !imageinfo_from_text( imageinfo, (char *) selection_data->data ) ) iwindow_alert( widget, GTK_MESSAGE_ERROR ); break; default: g_assert( FALSE ); break; } } static void colourdisplay_generate_tooltip( Colourdisplay *colourdisplay, VipsBuf *buf ) { Imagedisplay *id = IMAGEDISPLAY( colourdisplay ); if( id->conv && id->conv->ii ) { imageinfo_to_text( id->conv->ii, buf ); vips_buf_appends( buf, "\n" ); vips_buf_appends( buf, _( "Double-click to edit this color, or " "drag-and-drop between colors" ) ); } } static void colourdisplay_conversion_changed( Imagedisplay *id ) { Colourdisplay *colourdisplay = COLOURDISPLAY( id ); IMAGEDISPLAY_CLASS( parent_class )->conversion_changed( id ); if( id->conv ) conversion_set_mag( id->conv, 5000 ); colourdisplay_set_drag_type( colourdisplay ); } static void colourdisplay_class_init( ColourdisplayClass *class ) { GtkWidgetClass *widget_class = (GtkWidgetClass *) class; ImagedisplayClass *imagedisplay_class = (ImagedisplayClass *) class; parent_class = g_type_class_peek_parent( class ); widget_class->realize = colourdisplay_realize; widget_class->drag_begin = colourdisplay_drag_begin; widget_class->drag_end = colourdisplay_drag_end; widget_class->drag_data_get = colourdisplay_drag_data_get; widget_class->drag_data_received = colourdisplay_drag_data_received; imagedisplay_class->conversion_changed = colourdisplay_conversion_changed; } static void colourdisplay_init( Colourdisplay *colourdisplay ) { #ifdef DEBUG printf( "colourdisplay_init\n" ); #endif /*DEBUG*/ /* Who wants to focus one of these :/ */ GTK_WIDGET_UNSET_FLAGS( GTK_WIDGET( colourdisplay ), GTK_CAN_FOCUS ); set_tooltip_generate( GTK_WIDGET( colourdisplay ), (TooltipGenerateFn) colourdisplay_generate_tooltip, NULL, NULL ); } GtkType colourdisplay_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Colourdisplay", sizeof( Colourdisplay ), sizeof( ColourdisplayClass ), (GtkClassInitFunc) colourdisplay_class_init, (GtkObjectInitFunc) colourdisplay_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_IMAGEDISPLAY, &info ); } return( type ); } Colourdisplay * colourdisplay_new( Conversion *conv ) { Colourdisplay *colourdisplay = gtk_type_new( TYPE_COLOURDISPLAY ); if( !conv ) conv = conversion_new( NULL ); conversion_set_synchronous( conv, TRUE ); imagedisplay_set_conversion( IMAGEDISPLAY( colourdisplay ), conv ); return( colourdisplay ); } ================================================ FILE: src/colourdisplay.h ================================================ /* subclass imagedisplay ... show a patch of plain colour from a 1x1 pixel * imageinfo */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_COLOURDISPLAY (colourdisplay_get_type()) #define COLOURDISPLAY( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_COLOURDISPLAY, Colourdisplay )) #define COLOURDISPLAY_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_COLOURDISPLAY, ColourdisplayClass )) #define IS_COLOURDISPLAY( obj ) (GTK_CHECK_TYPE( (obj), TYPE_COLOURDISPLAY )) #define IS_COLOURDISPLAY_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_COLOURDISPLAY )) typedef struct _Colourdisplay { Imagedisplay parent_class; /* Set this to indicate that we prefer to drag as text rather than * colour. */ gboolean drag_as_text; } Colourdisplay; typedef struct _ColourdisplayClass { ImagedisplayClass parent_class; /* My methods. */ } ColourdisplayClass; GtkType colourdisplay_get_type( void ); Colourdisplay *colourdisplay_new( Conversion *conv ); ================================================ FILE: src/colourview.c ================================================ /* run the display for an image in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; static void colourview_link( View *view, Model *model, View *parent ) { Colourview *colourview = COLOURVIEW( view ); Rowview *rview = ROWVIEW( parent->parent ); VIEW_CLASS( parent_class )->link( view, model, parent ); rowview_menu_attach( rview, GTK_WIDGET( colourview->colourdisplay ) ); } static void colourview_refresh( vObject *vobject ) { Colourview *colourview = COLOURVIEW( vobject ); Colour *colour = COLOUR( vobject->iobject ); #ifdef DEBUG printf( "colourview_refresh\n" ); #endif /*DEBUG*/ conversion_set_image( colourview->conv, colour_ii_new( colour ) ); set_gcaption( colourview->label, "%s", vips_buf_all( &colour->caption ) ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void colourview_class_init( ColourviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = colourview_refresh; view_class->link = colourview_link; } static void colourview_area_changed_cb( Imagedisplay *id, Rect *area, Colourview *colourview ) { double rgb[4]; imageinfo_to_rgb( id->conv->ii, rgb ); colour_set_rgb( COLOUR( VOBJECT( colourview )->iobject ), rgb ); } static void colourview_doubleclick_one_cb( GtkWidget *widget, GdkEvent *event, Colourview *colourview ) { Heapmodel *heapmodel = HEAPMODEL( VOBJECT( colourview )->iobject ); Row *row = heapmodel->row; row_select_modifier( row, event->button.state ); } static void colourview_doubleclick_two_cb( GtkWidget *widget, GdkEvent *event, Colourview *colourview ) { model_edit( widget, MODEL( VOBJECT( colourview )->iobject ) ); } static void colourview_init( Colourview *colourview ) { GtkWidget *eb; GtkWidget *vbox; #ifdef DEBUG printf( "colourview_init\n" ); #endif /*DEBUG*/ eb = gtk_event_box_new(); gtk_widget_add_events( GTK_WIDGET( eb ), GDK_POINTER_MOTION_HINT_MASK ); gtk_box_pack_start( GTK_BOX( colourview ), eb, FALSE, FALSE, 0 ); vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( eb ), vbox ); gtk_widget_show( vbox ); colourview->colourdisplay = colourdisplay_new( NULL ); colourview->conv = IMAGEDISPLAY( colourview->colourdisplay )->conv; gtk_widget_set_size_request( GTK_WIDGET( colourview->colourdisplay ), DISPLAY_THUMBNAIL, DISPLAY_THUMBNAIL ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( colourview->colourdisplay ), FALSE, FALSE, 0 ); g_signal_connect( colourview->colourdisplay, "area_changed", G_CALLBACK( colourview_area_changed_cb ), colourview ); gtk_widget_show( GTK_WIDGET( colourview->colourdisplay ) ); colourview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( colourview->label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( colourview->label ), 2, 0 ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( colourview->label ), FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( colourview->label ) ); doubleclick_add( GTK_WIDGET( colourview ), FALSE, DOUBLECLICK_FUNC( colourview_doubleclick_one_cb ), colourview, DOUBLECLICK_FUNC( colourview_doubleclick_two_cb ), colourview ); gtk_widget_set_name( eb, "caption_widget" ); gtk_widget_show( eb ); } GtkType colourview_get_type( void ) { static GtkType colourview_type = 0; if( !colourview_type ) { static const GtkTypeInfo info = { "Colourview", sizeof( Colourview ), sizeof( ColourviewClass ), (GtkClassInitFunc) colourview_class_init, (GtkObjectInitFunc) colourview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; colourview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( colourview_type ); } View * colourview_new( void ) { Colourview *colourview = gtk_type_new( TYPE_COLOURVIEW ); return( VIEW( colourview ) ); } ================================================ FILE: src/colourview.h ================================================ /* a colourview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_COLOURVIEW (colourview_get_type()) #define COLOURVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_COLOURVIEW, Colourview )) #define COLOURVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_COLOURVIEW, ColourviewClass )) #define IS_COLOURVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_COLOURVIEW )) #define IS_COLOURVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_COLOURVIEW )) typedef struct _Colourview { Graphicview parent_object; Colourdisplay *colourdisplay; Conversion *conv; GtkWidget *label; } Colourview; typedef struct _ColourviewClass { GraphicviewClass parent_class; /* My methods. */ } ColourviewClass; GtkType colourview_get_type( void ); View *colourview_new( void ); ================================================ FILE: src/column.c ================================================ /* a column button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static FilemodelClass *parent_class = NULL; /* Offset for this column load/save. */ static int column_left_offset = 0; static int column_top_offset = 0; /* When we merge workspaces we need to scroll to position the last new column * in view. */ static Column *column_last_new = NULL; /* Map down a column. */ void * column_map( Column *col, row_map_fn fn, void *a, void *b ) { Subcolumn *scol = col->scol; return( subcolumn_map( scol, fn, a, b ) ); } void * column_map_symbol_sub( Row *row, symbol_map_fn fn, void *a ) { return( fn( row->sym, a, NULL, NULL ) ); } /* Map down a column, applying to the symbol of the row. */ void * column_map_symbol( Column *col, symbol_map_fn fn, void *a ) { return( column_map( col, (row_map_fn) column_map_symbol_sub, (void *) fn, a ) ); } static void column_finalize( GObject *gobject ) { Column *col; #ifdef DEBUG printf( "column_finalize\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_COLUMN( gobject ) ); col = COLUMN( gobject ); if( col == column_last_new ) column_last_new = NULL; IM_FREEF( g_source_remove, col->scrollto_timeout ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Select all things in a column. */ void * column_select_symbols( Column *col ) { return( column_map( col, (row_map_fn) row_select_extend, NULL, NULL ) ); } static Subcolumn * column_get_subcolumn( Column *col ) { g_assert( g_slist_length( ICONTAINER( col )->children ) == 1 ); return( SUBCOLUMN( ICONTAINER( col )->children->data ) ); } static void column_child_add( iContainer *parent, iContainer *child, int pos ) { Column *col = COLUMN( parent ); ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos ); /* Update our context. */ col->scol = column_get_subcolumn( col ); } static void column_child_remove( iContainer *parent, iContainer *child ) { Column *col = COLUMN( parent ); workspace_set_modified( col->ws, TRUE ); ICONTAINER_CLASS( parent_class )->child_remove( parent, child ); } static Workspace * column_get_workspace( Column *col ) { return( WORKSPACE( ICONTAINER( col )->parent ) ); } static void column_parent_add( iContainer *child ) { Column *col = COLUMN( child ); g_assert( IS_WORKSPACE( child->parent ) ); ICONTAINER_CLASS( parent_class )->parent_add( child ); g_assert( IS_WORKSPACE( child->parent ) ); /* Update our context. */ col->ws = column_get_workspace( col ); g_assert( IS_WORKSPACE( child->parent ) ); } static View * column_view_new( Model *model, View *parent ) { if( IS_PREFWORKSPACEVIEW( parent ) ) return( prefcolumnview_new() ); else return( columnview_new() ); } static xmlNode * column_save( Model *model, xmlNode *xnode ) { Column *col = COLUMN( model ); int x = IM_MAX( 0, col->x - column_left_offset ); int y = IM_MAX( 0, col->y - column_top_offset ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); /* Save sform for backwards compat with nip 7.8 ... now a workspace * property. */ if( !set_iprop( xthis, "x", x ) || !set_iprop( xthis, "y", y ) || !set_sprop( xthis, "open", bool_to_char( col->open ) ) || !set_sprop( xthis, "selected", bool_to_char( col->selected ) ) || !set_sprop( xthis, "sform", bool_to_char( FALSE ) ) || !set_iprop( xthis, "next", col->next ) || !set_sprop( xthis, "name", IOBJECT( col )->name ) ) return( NULL ); /* Caption can be NULL for untitled columns. */ if( IOBJECT( col )->caption ) if( !set_sprop( xthis, "caption", IOBJECT( col )->caption ) ) return( NULL ); return( xthis ); } static gboolean column_save_test( Model *model ) { Column *col = COLUMN( model ); Workspace *ws = col->ws; Workspacegroup *wsg = workspace_get_workspacegroup( ws ); if( wsg->save_type == WORKSPACEGROUP_SAVE_SELECTED ) /* Only save columns containing selected rows. */ return( column_map( col, (row_map_fn) row_is_selected, NULL, NULL ) != NULL ); return( TRUE ); } static void column_set_last_new( Column *col ) { if( !column_last_new ) column_last_new = col; } static gboolean column_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { Column *col = COLUMN( model ); int x = col->x; int y = col->y; char buf[256]; g_assert( IS_WORKSPACE( parent ) ); if( !get_iprop( xnode, "x", &x ) || !get_iprop( xnode, "y", &y ) || !get_bprop( xnode, "open", &col->open ) || !get_bprop( xnode, "selected", &col->selected ) || !get_iprop( xnode, "next", &col->next ) ) return( FALSE ); col->x = x + column_left_offset; col->y = y + column_top_offset; /* Don't use iobject_set(): we don't want to trigger _changed during * load. */ if( get_sprop( xnode, "caption", buf, 256 ) ) { IM_SETSTR( IOBJECT( col )->caption, buf ); } if( get_sprop( xnode, "name", buf, 256 ) ) { IM_SETSTR( IOBJECT( col )->name, buf ); } column_set_last_new( col ); return( MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ); } static void column_class_init( ColumnClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; FilemodelClass *filemodel_class = (FilemodelClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = column_finalize; /* Create signals. */ /* Init methods. */ iobject_class->user_name = _( "Column" ); icontainer_class->child_add = column_child_add; icontainer_class->child_remove = column_child_remove; icontainer_class->parent_add = column_parent_add; model_class->view_new = column_view_new; model_class->save = column_save; model_class->save_test = column_save_test; model_class->load = column_load; filemodel_class->filetype = filesel_type_workspace; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void column_init( Column *col ) { #ifdef DEBUG printf( "column_init\n" ); #endif /*DEBUG*/ col->scol = NULL; col->ws = NULL; col->x = 0; col->y = 0; col->open = TRUE; col->selected = FALSE; col->next = 1; col->last_select = NULL; } GType column_get_type( void ) { static GType column_type = 0; if( !column_type ) { static const GTypeInfo info = { sizeof( ColumnClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) column_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Column ), 32, /* n_preallocs */ (GInstanceInitFunc) column_init, }; column_type = g_type_register_static( TYPE_FILEMODEL, "Column", &info, 0 ); } return( column_type ); } Column * column_new( Workspace *ws, const char *name ) { Column *col; if( workspace_column_find( ws, name ) ) { error_top( _( "Name clash." ) ); error_sub( _( "Can't create column \"%s\". A column with that " "name already exists." ), name ); return( NULL ); } col = COLUMN( g_object_new( TYPE_COLUMN, NULL ) ); iobject_set( IOBJECT( col ), name, NULL ); icontainer_child_add( ICONTAINER( ws ), ICONTAINER( col ), -1 ); subcolumn_new( NULL, col ); col->x = ws->vp.left + 50; col->y = ws->vp.top; column_set_last_new( col ); return( col ); } Column * column_get_last_new( void ) { return( column_last_new ); } void column_clear_last_new( void ) { column_last_new = NULL; } /* Find the bottom of the column. */ Row * column_get_bottom( Column *col ) { Subcolumn *scol = col->scol; GSList *children = ICONTAINER( scol )->children; if( children ) { Row *row = ROW( g_slist_last( children )->data ); return( row ); } return( NULL ); } /* Add the last n names from a column to a buffer. Error if there are too few * there. */ gboolean column_add_n_names( Column *col, const char *name, VipsBuf *buf, int nparam ) { Subcolumn *scol = col->scol; GSList *children = ICONTAINER( scol )->children; int len = g_slist_length( children ); GSList *i; g_assert( nparam >= 0 ); if( nparam > 0 && nparam > len ) { error_top( _( "Too few items." ) ); error_sub( _( "This column only has %d items, " "but %s needs %d items." ), len, name, nparam ); return( FALSE ); } for( i = g_slist_nth( children, len - nparam ); i; i = i->next ) { Row *row = ROW( i->data ); if( row->sym ) { vips_buf_appends( buf, " " ); vips_buf_appends( buf, IOBJECT( row->sym )->name ); } } return( TRUE ); } /* Is a column empty? */ gboolean column_is_empty( Column *col ) { Subcolumn *scol = col->scol; GSList *children = ICONTAINER( scol )->children; return( children == NULL ); } /* Set the load/save offsets. */ void column_set_offset( int x_off, int y_off ) { #ifdef DEBUG printf( "column_set_offset: load offset %d x %d\n", x_off, y_off ); #endif /*DEBUG*/ column_left_offset = x_off; column_top_offset = y_off; } char * column_name_new( Column *col ) { char buf[256]; do { im_snprintf( buf, 256, "%s%d", IOBJECT( col )->name, col->next++ ); } while( compile_lookup( col->ws->sym->expr->compile, buf ) ); return( im_strdup( NULL, buf ) ); } void column_set_open( Column *col, gboolean open ) { if( col->open != open ) { Workspace *ws = col->ws; col->open = open; workspace_set_modified( ws, TRUE ); iobject_changed( IOBJECT( col ) ); } } static gboolean column_scrollto_timeout_cb( Column *col ) { #ifdef DEBUG printf( "column_scrollto_timeout_cb: %p\n", col ); #endif /*DEBUG*/ col->scrollto_timeout = 0; model_scrollto( MODEL( col ), col->pending_position ); return( FALSE ); } void column_scrollto( Column *col, ModelScrollPosition position ) { #ifdef DEBUG printf( "column_scrollto: %p %s\n", col, IOBJECT( col )->name ); #endif /*DEBUG*/ IM_FREEF( g_source_remove, col->scrollto_timeout ); col->pending_position = position; /* We need a longer timeout here than the one in mainw_layout(). */ col->scrollto_timeout = g_timeout_add( 400, (GSourceFunc) column_scrollto_timeout_cb, col ); } ================================================ FILE: src/column.h ================================================ /* a column in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_COLUMN (column_get_type()) #define COLUMN( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_COLUMN, Column )) #define COLUMN_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_COLUMN, ColumnClass)) #define IS_COLUMN( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_COLUMN )) #define IS_COLUMN_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_COLUMN )) #define COLUMN_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_COLUMN, ColumnClass )) struct _Column { Filemodel parent_object; /* Our context. */ Subcolumn *scol; /* Subcolumn we enclose */ Workspace *ws; /* Enclosing workspace */ /* Appearance state info. */ int x, y; /* Position */ gboolean open; /* Currently popped down */ gboolean selected; /* Other state. */ int next; /* Index of next symbol we make */ Row *last_select; /* Last row clicked ... for x sel */ /* A pending scrollto. */ guint scrollto_timeout; ModelScrollPosition pending_position; }; typedef struct _ColumnClass { FilemodelClass parent_class; /* My methods. */ } ColumnClass; void *column_map( Column *col, row_map_fn fn, void *a, void *b ); void *column_map_symbol( Column *col, symbol_map_fn fn, void *a ); void *column_select_symbols( Column *col ); GtkType column_get_type( void ); Column *column_new( Workspace *ws, const char *name ); Column *column_get_last_new( void ); void column_clear_last_new( void ); Row *column_get_bottom( Column *col ); gboolean column_add_n_names( Column *col, const char *name, VipsBuf *buf, int nparam ); gboolean column_is_empty( Column *col ); void column_set_offset( int x_off, int y_off ); char *column_name_new( Column *col ); void column_set_open( Column *col, gboolean open ); void column_scrollto( Column *col, ModelScrollPosition position ); ================================================ FILE: src/columnview.c ================================================ /* a view of a column */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; /* The columnview popup menu. */ static GtkWidget *columnview_menu = NULL; /* Edit caption ... right button menu on title bar. */ static void columnview_caption_cb( GtkWidget *wid, GtkWidget *host, Columnview *cview ) { /* Edit caption! */ if( cview->state == COL_EDIT ) return; cview->state = COL_EDIT; vobject_refresh_queue( VOBJECT( cview ) ); } /* Select all objects in menu's column. */ static void columnview_select_cb( GtkWidget *wid, GtkWidget *host, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); Workspace *ws = col->ws; workspace_deselect_all( ws ); column_select_symbols( col ); } /* Clone a column. */ static void columnview_clone_cb( GtkWidget *wid, GtkWidget *host, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); Workspace *ws = col->ws; char new_name[MAX_STRSIZE]; Column *newcol; workspace_column_name_new( ws, new_name ); newcol = workspace_column_get( ws, new_name ); iobject_set( IOBJECT( newcol ), NULL, IOBJECT( col )->caption ); newcol->x = col->x + 100; newcol->y = col->y; workspace_deselect_all( ws ); column_select_symbols( col ); workspace_column_select( ws, newcol ); if( !workspace_selected_duplicate( ws ) ) iwindow_alert( GTK_WIDGET( cview ), GTK_MESSAGE_ERROR ); workspace_deselect_all( ws ); symbol_recalculate_all(); } static void columnview_merge_sub( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Column *col = COLUMN( client ); Workspace *ws = col->ws; Workspacegroup *wsg = workspace_get_workspacegroup( ws ); char *filename; iWindowResult result; result = IWINDOW_YES; progress_begin(); if( (filename = filesel_get_filename( filesel )) ) { if( !workspacegroup_merge_rows( wsg, filename ) ) result = IWINDOW_ERROR; g_free( filename ); } symbol_recalculate_all(); progress_end(); nfn( sys, result ); } static void columnview_merge_cb( GtkWidget *wid, GtkWidget *host, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( cview ) ) ); GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Merge Into Column \"%s\"" ), IOBJECT( col )->name ); filesel_set_flags( FILESEL( filesel ), FALSE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) ); idialog_set_iobject( IDIALOG( filesel ), IOBJECT( col ) ); filesel_set_done( FILESEL( filesel ), columnview_merge_sub, col ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } /* Callback from save browser. */ static void columnview_save_as_sub( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Column *col = COLUMN( client ); Workspace *ws = col->ws; char *filename; workspace_deselect_all( ws ); column_select_symbols( col ); if( (filename = filesel_get_filename( filesel )) ) { if( workspace_selected_save( ws, filename ) ) { workspace_deselect_all( ws ); nfn( sys, IWINDOW_YES ); } else nfn( sys, IWINDOW_ERROR ); g_free( filename ); } else nfn( sys, IWINDOW_ERROR ); } /* Save a column ... can't just do view_save_as_cb(), since we need to save * the enclosing workspace too. Hence we have to save_selected on the ws, but * only after we have the filename. */ static void columnview_save_as_cb( GtkWidget *wid, GtkWidget *host, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( cview ) ) ); GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Save Column \"%s\"" ), IOBJECT( col )->name ); filesel_set_flags( FILESEL( filesel ), FALSE, TRUE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) ); idialog_set_iobject( IDIALOG( filesel ), IOBJECT( col ) ); filesel_set_done( FILESEL( filesel ), columnview_save_as_sub, col ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } /* Make a name for a column menu file, based on what we're going to call the * menu item. */ static void columnview_filename( char *file, const char *caption ) { int i; char name[FILENAME_MAX]; im_strncpy( name, caption, 10 ); for( i = 0; i < strlen( name ); i++ ) if( name[i] == ' ' ) name[i] = '_'; for( i = 0; ; i++ ) { im_snprintf( file, FILENAME_MAX, "$SAVEDIR" G_DIR_SEPARATOR_S "data" G_DIR_SEPARATOR_S "%s-%d.ws", name, i ); if( !existsf( "%s", file ) ) break; } } /* Remember the name of the last toolkit the user asked to add to. */ static char *columnview_to_menu_last_toolkit = NULL; /* Done button hit. */ static void columnview_to_menu_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Column *col = COLUMN( client ); Workspace *ws = col->ws; Stringset *ss = STRINGSET( iwnd ); StringsetChild *name = stringset_child_get( ss, _( "Name" ) ); StringsetChild *toolkit = stringset_child_get( ss, _( "Toolkit" ) ); StringsetChild *file = stringset_child_get( ss, _( "Filename" ) ); char name_text[1024]; char toolkit_text[1024]; char file_text[1024]; if( !get_geditable_string( name->entry, name_text, 1024 ) || !get_geditable_name( toolkit->entry, toolkit_text, 1024 ) || !get_geditable_filename( file->entry, file_text, 1024 ) ) { nfn( sys, IWINDOW_ERROR ); return; } /* Save column to file. */ workspace_deselect_all( ws ); column_select_symbols( col ); if( !workspace_selected_save( ws, file_text ) ) { nfn( sys, IWINDOW_ERROR ); return; } workspace_deselect_all( ws ); if( !tool_new_dia( toolkit_by_name( ws->kitg, toolkit_text ), -1, name_text, file_text ) ) { unlinkf( "%s", file_text ); nfn( sys, IWINDOW_ERROR ); return; } IM_SETSTR( columnview_to_menu_last_toolkit, toolkit_text ); nfn( sys, IWINDOW_YES ); } /* Make a column into a menu item. */ static void columnview_to_menu_cb( GtkWidget *wid, GtkWidget *host, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( cview ) ) ); GtkWidget *ss = stringset_new(); char *name; char *kit_name; char filename[FILENAME_MAX]; if( !(name = IOBJECT( col )->caption) ) name = "untitled"; columnview_filename( filename, name ); if( columnview_to_menu_last_toolkit ) kit_name = columnview_to_menu_last_toolkit; else kit_name = "untitled"; stringset_child_new( STRINGSET( ss ), _( "Name" ), name, _( "Set menu item text here" ) ); stringset_child_new( STRINGSET( ss ), _( "Toolkit" ), kit_name, _( "Add to this toolkit" ) ); stringset_child_new( STRINGSET( ss ), _( "Filename" ), filename, _( "Store column in this file" ) ); iwindow_set_title( IWINDOW( ss ), _( "New Menu Item from Column \"%s\"" ), IOBJECT( col )->name ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, col ); idialog_set_help_tag( IDIALOG( ss ), "sec:diaref" ); idialog_add_ok( IDIALOG( ss ), columnview_to_menu_done_cb, _( "Menuize" ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( iwnd ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } /* Find the position and size of a columnview. */ void columnview_get_position( Columnview *cview, int *x, int *y, int *w, int *h ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); if( GTK_WIDGET( cview )->allocation.x < 2 || GTK_WIDGET( cview )->allocation.y < 2 ) { /* Nothing there yet, guess. */ *x = col->x; *y = col->y; *w = 200; *h = 50; } else { *x = GTK_WIDGET( cview )->allocation.x; *y = GTK_WIDGET( cview )->allocation.y; *w = GTK_WIDGET( cview )->allocation.width; *h = GTK_WIDGET( cview )->allocation.height; #ifdef DEBUG printf( "columnview_get_position: %s, " "x = %d, y = %d, w = %d, h = %d\n", IOBJECT( col )->name, *x, *y, *w, *h ); #endif /*DEBUG*/ } } /* Transition functions for mouse stuff on columnviews. */ static void columnview_left_press( Columnview *cview, GdkEvent *ev ) { Workspaceview *wview = cview->wview; int ix, iy; int jx, jy; int kx, ky; int wx, wy, ww, wh; #ifdef DEBUG printf( "columnview_left_press\n" ); #endif /*DEBUG*/ /* Find pos of columnview. */ columnview_get_position( cview, &wx, &wy, &ww, &wh ); /* Position in virtual tally window. */ ix = ev->button.x + wx; iy = ev->button.y + wy; /* Position in tally viewport. */ jx = ix - wview->vp.left; jy = iy - wview->vp.top; /* So ... position of top LH corner of tally viewport in root window. */ kx = ev->button.x_root - jx; ky = ev->button.y_root - jy; switch( cview->state ) { case COL_WAIT: cview->state = COL_SELECT; /* Record offset of mouse in columnview title bar. */ cview->rx = ev->button.x; cview->ry = ev->button.y; /* Position of tally window in root window. */ cview->tx = kx; cview->ty = ky; /* Start position of mouse in virtual tally window. */ cview->sx = ix; cview->sy = iy; break; case COL_SELECT: case COL_DRAG: case COL_EDIT: break; default: g_assert( FALSE ); } } static void columnview_add_shadow( Columnview *old_cview ) { Column *col = COLUMN( VOBJECT( old_cview )->iobject ); Workspaceview *wview = old_cview->wview; if( !old_cview->shadow ) { Columnview *new_cview; new_cview = COLUMNVIEW( columnview_new() ); new_cview->wview = wview; VIEW( new_cview )->parent = VIEW( wview ); VOBJECT( new_cview )->iobject = IOBJECT( col ); gtk_fixed_put( GTK_FIXED( wview->fixed ), GTK_WIDGET( new_cview ), col->x, col->y ); gtk_widget_show( GTK_WIDGET( new_cview ) ); old_cview->shadow = new_cview; new_cview->master = old_cview; /* The shadow will be on top of the real column and hide it. * Put the real column to the front. */ model_front( MODEL( col ) ); } } static void columnview_left_motion( Columnview *cview, GdkEvent *ev ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); Workspace *ws = col->ws; Workspaceview *wview = cview->wview; int u, v; /* Posn of pointer in tally viewport. */ int ix = ev->motion.x_root - cview->tx; int iy = ev->motion.y_root - cview->ty; /* Posn in virtual tally cods. */ int jx = ix + wview->vp.left; int jy = iy + wview->vp.top; /* Amount of drag since we started. */ int xoff = jx - cview->sx; int yoff = jy - cview->sy; /* New columnview position. */ int xnew = IM_MAX( 0, jx - cview->rx ); int ynew = IM_MAX( 0, jy - cview->ry ); #ifdef DEBUG printf( "columnview_left_motion\n" ); #endif /*DEBUG*/ switch( cview->state ) { case COL_EDIT: case COL_WAIT: break; case COL_SELECT: /* How much drag? */ if( abs( xoff ) > 5 || abs( yoff ) > 5 ) { cview->state = COL_DRAG; workspaceview_set_cursor( wview, IWINDOW_SHAPE_MOVE ); gtk_grab_add( cview->title ); columnview_add_shadow( cview ); } break; case COL_DRAG: col->x = xnew; col->y = ynew; iobject_changed( IOBJECT( col ) ); #ifdef DEBUG printf( "drag columnview: x=%d, y=%d", col->x, col->y ); #endif /*DEBUG*/ /* Set vars for bg scroll. */ u = 0; if( ix > wview->vp.width ) u = 10; else if( ix < 0 ) u = -10; v = 0; if( iy > wview->vp.height ) v = 10; else if( iy < 0 ) v = -10; workspaceview_scroll_background( wview, u, v ); /* Move other columns about. */ model_layout( MODEL( ws ) ); break; default: g_assert( FALSE ); } } static void columnview_left_release( Columnview *cview, GdkEvent *ev ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); Workspace *ws = col->ws; Workspaceview *wview = cview->wview; #ifdef DEBUG printf( "columnview_left_release\n" ); #endif /*DEBUG*/ /* Back to wait. */ switch( cview->state ) { case COL_SELECT: cview->state = COL_WAIT; workspace_column_select( ws, col ); break; case COL_DRAG: cview->state = COL_WAIT; workspaceview_scroll_background( wview, 0, 0 ); workspaceview_set_cursor( wview, IWINDOW_SHAPE_NONE ); gtk_grab_remove( cview->title ); DESTROY_GTK( cview->shadow ); /* Move columns to their final position. */ model_layout( MODEL( ws ) ); workspace_set_modified( ws, TRUE ); break; case COL_EDIT: case COL_WAIT: break; default: g_assert( FALSE ); } } /* Event in columnview title bar. */ static gboolean columnview_title_event_cb( GtkWidget *widget, GdkEvent *ev, Columnview *cview ) { gboolean handled = FALSE; #ifdef DEBUG { Column *col = COLUMN( VOBJECT( cview )->iobject ); printf( "columnview_title_event_cb: %s %d\n", IOBJECT( col )->name, ev->type ); } #endif /*DEBUG*/ switch( ev->type ) { case GDK_BUTTON_PRESS: if( ev->button.button == 1 ) { columnview_left_press( cview, ev ); handled = TRUE; } break; case GDK_2BUTTON_PRESS: if( ev->button.button == 1 ) { if( cview->state != COL_EDIT ) { cview->state = COL_EDIT; vobject_refresh_queue( VOBJECT( cview ) ); } handled = TRUE; } break; case GDK_MOTION_NOTIFY: if( ev->motion.state & GDK_BUTTON1_MASK ) { columnview_left_motion( cview, ev ); handled = TRUE; } break; case GDK_BUTTON_RELEASE: if( ev->button.button == 1 ) { columnview_left_release( cview, ev ); handled = TRUE; } break; default: break; } return( handled ); } static void columnview_destroy( GtkObject *object ) { Columnview *cview; Column *col; g_return_if_fail( object != NULL ); g_return_if_fail( IS_COLUMNVIEW( object ) ); cview = COLUMNVIEW( object ); col = COLUMN( VOBJECT( cview )->iobject ); #ifdef DEBUG printf( "columnview_destroy:\n" ); #endif /*DEBUG*/ DESTROY_GTK( cview->shadow ); /* The column has gone .. relayout. */ if( col && col->ws ) { workspace_set_needs_layout( col->ws, TRUE ); mainw_layout(); } GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void columnview_size_allocate( GtkWidget *widget, GtkAllocation *allocation ) { Columnview *cview = COLUMNVIEW( widget ); if( cview->old_width != allocation->width || cview->old_height != allocation->height ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); Workspace *ws = col->ws; cview->old_width = allocation->width; cview->old_height = allocation->height; workspace_set_needs_layout( ws, TRUE ); mainw_layout(); } GTK_WIDGET_CLASS( parent_class )->size_allocate( widget, allocation ); } /* Arrow button on title bar. */ static void columnview_updown_cb( GtkWidget *wid, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); column_set_open( col, !col->open ); } /* Delete this column from the popup menu. */ static void columnview_destroy_cb( GtkWidget *wid, GtkWidget *host, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); model_check_destroy( view_get_toplevel( VIEW( cview ) ), MODEL( col ), NULL ); } /* Delete this column with a click on the 'x' button. */ static void columnview_destroy2_cb( GtkWidget *wid, Columnview *cview ) { Column *col = COLUMN( VOBJECT( cview )->iobject ); model_check_destroy( view_get_toplevel( VIEW( cview ) ), MODEL( col ), NULL ); } /* Callback for enter in caption edit box. */ static void columnview_caption_enter_cb( GtkWidget *wid, Columnview *cview ) { const char *text = gtk_entry_get_text( GTK_ENTRY( cview->capedit ) ); Column *col = COLUMN( VOBJECT( cview )->iobject ); Workspace *ws = col->ws; cview->state = COL_WAIT; iobject_changed( IOBJECT( col ) ); if( strcmp( text, "" ) != 0 ) iobject_set( IOBJECT( col ), NULL, text ); workspace_set_modified( ws, TRUE ); /* The ws view needs to update the jumpto menus. */ iobject_changed( IOBJECT( ws ) ); } /* Detect cancel in a caption field. */ static gboolean columnview_caption_cancel_cb( GtkWidget *widget, GdkEvent *ev, Columnview *cview ) { if( ev->type != GDK_KEY_PRESS || ev->key.keyval != GDK_Escape ) return( FALSE ); /* Turn off edit. */ cview->state = COL_WAIT; vobject_refresh_queue( VOBJECT( cview ) ); return( TRUE ); } /* Add a caption entry to a columnview if not there. */ static void columnview_add_caption( Columnview *cview ) { if( cview->capedit ) return; cview->capedit = gtk_entry_new(); gtk_entry_set_has_frame( GTK_ENTRY( cview->capedit ), FALSE ); gtk_box_pack_start( GTK_BOX( cview->titlehb ), cview->capedit, FALSE, FALSE, 0 ); set_tooltip( cview->capedit, _( "Edit caption, press enter to " "accept changes, press escape to cancel" ) ); gtk_signal_connect( GTK_OBJECT( cview->capedit ), "activate", GTK_SIGNAL_FUNC( columnview_caption_enter_cb ), cview ); gtk_signal_connect( GTK_OBJECT( cview->capedit ), "event", GTK_SIGNAL_FUNC( columnview_caption_cancel_cb ), cview ); } /* Callback for enter in new def widget. */ static void columnview_text_enter_cb( GtkWidget *wid, Columnview *cview ) { const char *text = gtk_entry_get_text( GTK_ENTRY( cview->text ) ); Column *col = COLUMN( VOBJECT( cview )->iobject ); Workspace *ws = col->ws; Symbol *sym; if( !text || strspn( text, WHITESPACE ) == strlen( text ) ) return; if( !(sym = workspace_add_def_recalc( ws, text )) ) { iwindow_alert( wid, GTK_MESSAGE_ERROR ); symbol_recalculate_all(); return; } set_gentry( cview->text, NULL ); } /* Add bottom entry widget. */ static void columnview_add_text( Columnview *cview ) { GtkWidget *inv; if( cview->textfr ) return; cview->textfr = gtk_hbox_new( FALSE, 0 ); gtk_box_pack_end( GTK_BOX( cview->vbox ), cview->textfr, FALSE, FALSE, 0 ); inv = gtk_label_new( "" ); gtk_box_pack_start( GTK_BOX( cview->textfr ), inv, FALSE, FALSE, 25 ); gtk_widget_show( inv ); cview->text = gtk_entry_new(); gtk_box_pack_start( GTK_BOX( cview->textfr ), cview->text, TRUE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( cview->text ), "activate", GTK_SIGNAL_FUNC( columnview_text_enter_cb ), cview ); gtk_widget_show( cview->text ); set_tooltip( cview->text, _( "Enter expressions here" ) ); } static void columnview_refresh( vObject *vobject ) { Columnview *cview = COLUMNVIEW( vobject ); Columnview *shadow = cview->shadow; Column *col = COLUMN( VOBJECT( cview )->iobject ); gboolean editable = col->ws->mode != WORKSPACE_MODE_NOEDIT; #ifdef DEBUG printf( "columnview_refresh: %s\n", IOBJECT( col )->name ); #endif /*DEBUG*/ /* If this column has a shadow, workspaceview will have put a layout * position into it. See workspaceview_layout_set_pos(). */ if( shadow ) view_child_position( VIEW( shadow ) ); if( shadow ) { gtk_widget_set_size_request( GTK_WIDGET( shadow->frame ), GTK_WIDGET( cview->frame )->allocation.width, GTK_WIDGET( cview->frame )->allocation.height ); gtk_frame_set_shadow_type( GTK_FRAME( shadow->frame ), GTK_SHADOW_IN ); } if( col->x != cview->lx || col->y != cview->ly ) { #ifdef DEBUG printf( "columnview_refresh: move column %s to %d x %d\n", IOBJECT( col )->name, col->x, col->y ); #endif /*DEBUG*/ cview->lx = col->x; cview->ly = col->y; view_child_position( VIEW( cview ) ); /* Update the save offset hints too. */ filemodel_set_offset( FILEMODEL( col ), cview->lx, cview->ly ); } /* Turn titlebar on/off. */ widget_visible( cview->title, editable ); if( editable ) gtk_frame_set_label( GTK_FRAME( cview->frame ), NULL ); else if( IOBJECT( col )->caption ) { GtkWidget *label; char buf[256]; char buf2[256]; gtk_frame_set_label( GTK_FRAME( cview->frame ), "x" ); label = gtk_frame_get_label_widget( GTK_FRAME( cview->frame ) ); escape_markup( IOBJECT( col )->caption, buf2, 256 ); im_snprintf( buf, 256, "%s", buf2 ); gtk_label_set_markup( GTK_LABEL( label ), buf ); gtk_misc_set_padding( GTK_MISC( label ), 2, 6 ); } /* Update names. */ set_glabel( cview->lab, "%s - ", IOBJECT( col )->name ); if( IOBJECT( col )->caption ) set_glabel( cview->head, "%s", IOBJECT( col )->caption ); else { char buf[256]; im_snprintf( buf, 256, "%s", _( "doubleclick to set title" ) ); gtk_label_set_markup( GTK_LABEL( cview->head ), buf ); } /* Set open/closed. */ if( col->open ) { gtk_arrow_set( GTK_ARROW( cview->updown ), GTK_ARROW_DOWN, GTK_SHADOW_OUT ); set_tooltip( cview->updownb, _( "Fold the column away" ) ); } else { gtk_arrow_set( GTK_ARROW( cview->updown ), GTK_ARROW_RIGHT, GTK_SHADOW_OUT ); set_tooltip( cview->updownb, _( "Open the column" ) ); } model_display( MODEL( col->scol ), col->open ); /* Closed columns are hidden in NOEDIT mode. */ widget_visible( GTK_WIDGET( cview ), editable || col->open ); /* Set caption edit. */ if( cview->state == COL_EDIT ) { columnview_add_caption( cview ); gtk_widget_show( cview->capedit ); gtk_widget_hide( cview->headfr ); if( IOBJECT( col )->caption ) { set_gentry( cview->capedit, "%s", IOBJECT( col )->caption ); gtk_editable_select_region( GTK_EDITABLE( cview->capedit ), 0, -1 ); } gtk_widget_grab_focus( cview->capedit ); } else { gtk_widget_show( cview->headfr ); DESTROY_GTK( cview->capedit ); } /* Set bottom entry. */ if( col->selected && col->open && editable && !cview->master ) { columnview_add_text( cview ); gtk_widget_show( cview->textfr ); } else DESTROY_GTK( cview->textfr ); /* Set select state. */ if( cview->master ) gtk_widget_set_name( cview->title, "shadow_widget" ); else if( col->selected && !cview->selected ) { gtk_widget_set_name( cview->title, "selected_widget" ); cview->selected = TRUE; if( cview->textfr ) gtk_widget_grab_focus( cview->text ); } else if( !col->selected ) { /* Always do this, even if cview->selected, so we set on the * first _refresh(). */ gtk_widget_set_name( cview->title, "column_widget" ); cview->selected = FALSE; } VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void columnview_link( View *view, Model *model, View *parent ) { Columnview *cview = COLUMNVIEW( view ); Workspaceview *wview = WORKSPACEVIEW( parent ); VIEW_CLASS( parent_class )->link( view, model, parent ); cview->wview = wview; } static void columnview_child_add( View *parent, View *child ) { Columnview *cview = COLUMNVIEW( parent ); Subcolumnview *sview = SUBCOLUMNVIEW( child ); VIEW_CLASS( parent_class )->child_add( parent, child ); gtk_container_add( GTK_CONTAINER( cview->frame ), GTK_WIDGET( sview ) ); } /* Scroll to keep the text entry at the bottom of the columnview on screen. * We can't use the position/size of the text widget for positioning, since it * may not be properly realized yet ... make the bottom of the column visible * instead. */ static void columnview_scrollto( View *view, ModelScrollPosition position ) { Columnview *cview = COLUMNVIEW( view ); Workspaceview *wview = cview->wview; int x, y, w, h; columnview_get_position( cview, &x, &y, &w, &h ); if( position == MODEL_SCROLL_BOTTOM ) /* 35 is supposed to be enough to ensure the whole of the edit * box gets on the screen. */ workspaceview_scroll( wview, x, y + h, w, 35 ); else workspaceview_scroll( wview, x, y, w, cview->title->allocation.height ); } static void columnview_class_init( ColumnviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; GtkWidget *pane; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ object_class->destroy = columnview_destroy; widget_class->size_allocate = columnview_size_allocate; vobject_class->refresh = columnview_refresh; view_class->link = columnview_link; view_class->child_add = columnview_child_add; view_class->scrollto = columnview_scrollto; pane = columnview_menu = popup_build( _( "Column menu" ) ); popup_add_but( pane, _( "_Edit Caption" ), POPUP_FUNC( columnview_caption_cb ) ); popup_add_but( pane, _( "Select _All" ), POPUP_FUNC( columnview_select_cb ) ); popup_add_but( pane, STOCK_DUPLICATE, POPUP_FUNC( columnview_clone_cb ) ); popup_add_but( pane, _( "Merge Into Column" ), POPUP_FUNC( columnview_merge_cb ) ); popup_add_but( pane, GTK_STOCK_SAVE_AS, POPUP_FUNC( columnview_save_as_cb ) ); menu_add_sep( pane ); popup_add_but( pane, _( "Make Column Into _Menu Item" ), POPUP_FUNC( columnview_to_menu_cb ) ); menu_add_sep( pane ); popup_add_but( pane, GTK_STOCK_DELETE, POPUP_FUNC( columnview_destroy_cb ) ); } static gboolean columnview_event_cb( GtkWidget *wid, GdkEvent *ev, Columnview *cview ) { gboolean handled; handled = FALSE; switch( ev->type ) { case GDK_BUTTON_PRESS: if( ev->button.button == 1 ) /* We want to sop our enclosing notebook seeing * left doubleclicks and creating new tabs. We want to * not block things like scroll events and * middle-drag. */ handled = TRUE; default: break; } return( handled ); } static void columnview_init( Columnview *cview ) { GtkWidget *sb; GtkWidget *frame; GtkWidget *icon; GtkWidget *but; /* No position yet. */ cview->lx = -1; cview->ly = -1; cview->state = COL_WAIT; cview->selected = FALSE; cview->old_width = -1; cview->old_height = -1; /* Make outer vb. */ cview->main = gtk_event_box_new(); gtk_widget_add_events( GTK_WIDGET( cview->main ), GDK_BUTTON_PRESS_MASK ); cview->vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( cview->main ), cview->vbox ); /* Frame for whole title bar. Need an event_box to catch clicks. */ cview->title = gtk_event_box_new(); gtk_widget_add_events( GTK_WIDGET( cview->title ), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ); gtk_box_pack_start( GTK_BOX( cview->vbox ), cview->title, FALSE, FALSE, 0 ); set_tooltip( cview->title, _( "Left-drag to move, left-double-click to " "set title, right-click for menu" ) ); frame = gtk_frame_new( NULL ); gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_NONE ); gtk_container_add( GTK_CONTAINER( cview->title ), frame ); popup_attach( cview->title, columnview_menu, cview ); gtk_signal_connect( GTK_OBJECT( cview->title ), "event", GTK_SIGNAL_FUNC( columnview_title_event_cb ), cview ); /* Layout contents of title bar. */ cview->titlehb = gtk_hbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( frame ), cview->titlehb ); /* Up/down button. */ cview->updownb = gtk_button_new(); gtk_button_set_relief( GTK_BUTTON( cview->updownb ), GTK_RELIEF_NONE ); gtk_container_set_border_width( GTK_CONTAINER( cview->updownb ), 0 ); gtk_box_pack_start( GTK_BOX( cview->titlehb ), cview->updownb, FALSE, FALSE, 0 ); cview->updown = gtk_arrow_new( GTK_ARROW_DOWN, GTK_SHADOW_OUT ); gtk_container_add( GTK_CONTAINER( cview->updownb ), cview->updown ); gtk_signal_connect( GTK_OBJECT( cview->updownb ), "clicked", GTK_SIGNAL_FUNC( columnview_updown_cb ), cview ); /* Remove columnview button. */ sb = gtk_vbox_new( FALSE, 0 ); gtk_box_pack_end( GTK_BOX( cview->titlehb ), sb, FALSE, FALSE, 1 ); but = gtk_button_new(); gtk_button_set_relief( GTK_BUTTON( but ), GTK_RELIEF_NONE ); gtk_box_pack_start( GTK_BOX( sb ), but, TRUE, FALSE, 0 ); set_tooltip( but, _( "Delete the column" ) ); icon = gtk_image_new_from_stock( GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU ); gtk_container_add( GTK_CONTAINER( but ), icon ); gtk_signal_connect( GTK_OBJECT( but ), "clicked", GTK_SIGNAL_FUNC( columnview_destroy2_cb ), cview ); /* Columnview name. */ cview->lab = gtk_label_new( "" ); gtk_box_pack_start( GTK_BOX( cview->titlehb ), cview->lab, FALSE, FALSE, 2 ); /* Comment. Wrap a frame around it, to make it the same size as * an entry widget. */ cview->headfr = gtk_frame_new( NULL ); gtk_frame_set_shadow_type( GTK_FRAME( cview->headfr ), GTK_SHADOW_NONE ); gtk_box_pack_start( GTK_BOX( cview->titlehb ), cview->headfr, FALSE, FALSE, 0 ); cview->head = gtk_label_new( "" ); gtk_container_add( GTK_CONTAINER( cview->headfr ), cview->head ); /* Make centre table for tally roll. */ cview->frame = gtk_frame_new( NULL ); gtk_frame_set_shadow_type( GTK_FRAME( cview->frame ), GTK_SHADOW_NONE ); gtk_box_pack_start( GTK_BOX( cview->vbox ), cview->frame, TRUE, TRUE, 0 ); gtk_box_pack_start( GTK_BOX( cview ), cview->main, FALSE, FALSE, 0 ); /* We need to stop our enclosing thing seeing doubeclicks and all * that. */ gtk_signal_connect( GTK_OBJECT( cview ), "event", GTK_SIGNAL_FUNC( columnview_event_cb ), cview ); gtk_widget_show_all( GTK_WIDGET( cview ) ); } GtkType columnview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Columnview", sizeof( Columnview ), sizeof( ColumnviewClass ), (GtkClassInitFunc) columnview_class_init, (GtkObjectInitFunc) columnview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VIEW, &info ); } return( type ); } View * columnview_new( void ) { Columnview *cview = gtk_type_new( TYPE_COLUMNVIEW ); return( VIEW( cview ) ); } ================================================ FILE: src/columnview.h ================================================ /* view of a column */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_COLUMNVIEW (columnview_get_type()) #define COLUMNVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_COLUMNVIEW, Columnview )) #define COLUMNVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_COLUMNVIEW, ColumnviewClass )) #define IS_COLUMNVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_COLUMNVIEW )) #define IS_COLUMNVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_COLUMNVIEW )) /* State ... for mouse titlebar interactions. */ typedef enum { COL_WAIT, /* Rest state */ COL_SELECT, /* Select start, but no drag yet */ COL_DRAG, /* Drag state */ COL_EDIT /* Editing caption */ } ColumnviewState; struct _Columnview { View view; /* Our enclosing workspaceview. */ Workspaceview *wview; /* Display parts. */ GtkWidget *main; /* Enclosing window for whole cview */ GtkWidget *lab; /* Columnview name label */ GtkWidget *vbox; /* Outermost vbox for cview */ GtkWidget *frame; /* Enclosing frame for tally stuff */ GtkWidget *title; /* Eventbox wrapper for title bar */ GtkWidget *titlehb; /* Title bar hbox */ GtkWidget *updown; /* Fold up/down arrow */ GtkWidget *updownb; /* Fold up/down button */ GtkWidget *head; /* Label on columnview */ GtkWidget *headfr; /* Frame wrapper around label */ GtkWidget *text; /* Text entry at bottom */ GtkWidget *textfr; /* Enclosing stuff for text entry */ GtkWidget *capedit; /* Shadow text for editing caption */ /* A shadow for this cview, used during drag to show where this column * will end up. * * And if we are a shadow, the master cview we are the shadow for. */ Columnview *shadow; Columnview *master; /* Appearance state info. */ int lx, ly; /* last pos we set */ ColumnviewState state; /* Waiting or dragging */ int sx, sy; /* Drag start point */ int rx, ry; /* Drag offset */ int tx, ty; /* Tally window pos in root cods */ gboolean selected; /* Last drawn in selected state? */ /* We watch resize events and trigger a workspace relayout with these. */ int old_width; int old_height; }; typedef struct _ColumnviewClass { ViewClass parent_class; /* My methods. */ } ColumnviewClass; void columnview_get_position( Columnview *cview, int *x, int *y, int *w, int *h ); GtkType columnview_get_type( void ); View *columnview_new( void ); ================================================ FILE: src/compile.c ================================================ /* Stuff to parse and compile text. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_RESOLVE */ /* regular (and very slow) sanity checks on symbols ... needs DEBUG in * symbol.c as well #define DEBUG_SANITY */ /* count how many nodes we find with common sub-expression removal. #define DEBUG_COMMON */ /* show what everything compiled to #define DEBUG_RESULT */ /* trace list comp compile #define DEBUG_LCOMP */ /* trace pattern LHS generation #define DEBUG_PATTERN */ /* #define DEBUG */ #include "ip.h" static iContainerClass *parent_class = NULL; Compile * compile_get_parent( Compile *compile ) { if( !ICONTAINER( compile->sym )->parent ) return( NULL ); return( COMPILE( ICONTAINER( compile->sym )->parent ) ); } void * compile_name_print( Compile *compile ) { printf( "compile(%p) ", compile ); symbol_name_print( compile->sym ); return( NULL ); } static void * compile_name_sub( Expr *expr, VipsBuf *buf ) { if( expr->row ) { if( !vips_buf_is_empty( buf ) ) vips_buf_appends( buf, ", " ); row_qualified_name( expr->row, buf ); } return( NULL ); } void compile_name( Compile *compile, VipsBuf *buf ) { char txt[256]; VipsBuf buf2 = VIPS_BUF_STATIC( txt ); vips_buf_appends( buf, "\"" ); symbol_qualified_name( compile->sym, buf ); vips_buf_appends( buf, "\"" ); slist_map( compile->exprs, (SListMapFn) compile_name_sub, &buf2 ); if( !vips_buf_is_empty( &buf2 ) ) vips_buf_appendf( buf, " (%s)", vips_buf_all( &buf2 ) ); } static Compile * compile_map_all_sub( Symbol *sym, map_compile_fn fn, void *a ) { if( !sym->expr || !sym->expr->compile ) return( NULL ); else return( compile_map_all( sym->expr->compile, fn, a ) ); } /* Apply a function to a compile ... and any local compiles. Do top-down. */ Compile * compile_map_all( Compile *compile, map_compile_fn fn, void *a ) { Compile *res; /* Us first. */ if( (res = fn( compile, a )) ) return( res ); /* Then any children. */ if( (res = (Compile *) icontainer_map( ICONTAINER( compile ), (icontainer_map_fn) compile_map_all_sub, (void *) fn, a )) ) return( res ); return( NULL ); } /* Look up by name. */ Symbol * compile_lookup( Compile *compile, const char *name ) { return( SYMBOL( icontainer_child_lookup( ICONTAINER( compile ), name ) ) ); } /* Make a dependency. Text in compile refers to sym. */ void compile_link_make( Compile *compile, Symbol *child ) { /* Already a dependency? Don't make a second link. */ if( !g_slist_find( compile->children, child ) ) { /* New link, each direction. */ compile->children = g_slist_prepend( compile->children, child ); child->parents = g_slist_prepend( child->parents, compile ); /* If the child is a forward reference, we may have to patch * this later. Save the pointer-to-child pointer on child. */ if( child->type == SYM_ZOMBIE ) (void) symbol_patch_add( &compile->children->data, child ); } #ifdef DEBUG_SANITY /* Sanity check. */ symbol_sanity( child ); symbol_leaf_set_sanity(); #endif /*DEBUG_SANITY*/ } /* Break a dependency. Text in compile referred to child. */ void * compile_link_break( Compile *compile, Symbol *child ) { /* Sanity check. */ #ifdef DEBUG_SANITY symbol_sanity( child ); symbol_leaf_set_sanity(); #endif /*DEBUG_SANITY*/ /* Must be there. */ g_assert( g_slist_find( compile->children, child ) && g_slist_find( child->parents, compile ) ); compile->children = g_slist_remove( compile->children, child ); child->parents = g_slist_remove( child->parents, compile ); /* Sanity check. */ #ifdef DEBUG_SANITY symbol_sanity( child ); symbol_leaf_set_sanity(); #endif /*DEBUG_SANITY*/ return( NULL ); } void * compile_expr_link_break( Compile *compile, Expr *expr ) { g_assert( expr->compile == compile ); g_assert( g_slist_find( compile->exprs, expr ) ); expr->compile = NULL; compile->exprs = g_slist_remove( compile->exprs, expr ); g_object_unref( G_OBJECT( compile ) ); return( NULL ); } void * compile_expr_link_break_rev( Expr *expr, Compile *compile ) { return( compile_expr_link_break( compile, expr ) ); } void compile_expr_link_make( Compile *compile, Expr *expr ) { g_assert( !expr->compile ); g_assert( !g_slist_find( compile->exprs, expr ) ); g_assert( compile->sym == expr->sym ); expr->compile = compile; compile->exprs = g_slist_prepend( compile->exprs, expr ); g_object_ref( G_OBJECT( compile ) ); iobject_sink( IOBJECT( compile ) ); } static void compile_finalize( GObject *gobject ) { Compile *compile; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_COMPILE( gobject ) ); compile = COMPILE( gobject ); #ifdef DEBUG printf( "compile_finalize: " ); compile_name_print( compile ); printf( "\n" ); #endif /*DEBUG*/ /* My instance destroy stuff. */ /* Junk parse tree. */ slist_map( compile->treefrag, (SListMapFn) tree_node_destroy, NULL ); IM_FREEF( g_slist_free, compile->treefrag ); compile->tree = NULL; /* Break links to all locals. */ IM_FREEF( g_slist_free, compile->param ); compile->nparam = 0; IM_FREEF( g_slist_free, compile->secret ); compile->nsecret = 0; compile->this = NULL; compile->super = NULL; (void) slist_map( compile->children, (SListMapFn) symbol_link_break, compile ); IM_FREEF( g_slist_free, compile->children ); /* Remove static strings we created. */ slist_map( compile->statics, (SListMapFn) managed_destroy_nonheap, NULL ); IM_FREEF( g_slist_free, compile->statics ); /* Junk heap. */ if( compile->heap ) { compile->base.type = ELEMENT_NOVAL; compile->base.ele = (void *) 1; heap_unregister_element( compile->heap, &compile->base ); UNREF( compile->heap ); } /* Junk text. */ IM_FREE( compile->text ); IM_FREE( compile->prhstext ); IM_FREE( compile->rhstext ); compile->sym = NULL; /* If we're being finalized, we must have a ref count of zero, so * there shouldn't be any exprs looking at us. */ g_assert( !compile->exprs ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void compile_class_init( CompileClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = compile_finalize; /* Create signals. */ /* Init default methods. */ } static void compile_init( Compile *compile ) { /* Init our instance fields. */ compile->sym = NULL; compile->exprs = NULL; compile->is_klass = FALSE; compile->has_super = FALSE; compile->text = NULL; compile->prhstext = NULL; compile->rhstext = NULL; compile->tree = NULL; compile->treefrag = NULL; compile->last_sym = NULL; compile->nparam = 0; compile->param = NULL; compile->nsecret = 0; compile->secret = NULL; compile->this = NULL; compile->super = NULL; compile->children = NULL; compile->base.type = ELEMENT_NOVAL; compile->heap = NULL; compile->statics = NULL; } GType compile_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( CompileClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) compile_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Compile ), 32, /* n_preallocs */ (GInstanceInitFunc) compile_init, }; type = g_type_register_static( TYPE_ICONTAINER, "Compile", &info, 0 ); } return( type ); } /* Make a compile linked to an expr. */ Compile * compile_new( Expr *expr ) { Compile *compile = COMPILE( g_object_new( TYPE_COMPILE, NULL ) ); compile->sym = expr->sym; /* Junk any old compile. */ if( expr->compile ) compile_expr_link_break( expr->compile, expr ); compile_expr_link_make( compile, expr ); /* We'll want to be able to do name lookups. */ icontainer_set_hash( ICONTAINER( compile ) ); #ifdef DEBUG printf( "compile_new: " ); compile_name_print( compile ); printf( "\n" ); #endif /*DEBUG*/ return( compile ); } /* Max cells function for symbols. Enough to compile something big. */ static int compile_heap_max_fn( Heap *heap ) { return( 10000 ); } /* Make a exprinfo suitable for a top-level symbol. */ Compile * compile_new_toplevel( Expr *expr ) { Compile *compile = compile_new( expr ); compile->heap = heap_new( compile, compile_heap_max_fn, 100, 1000 ); g_object_ref( G_OBJECT( compile->heap ) ); iobject_sink( IOBJECT( compile->heap ) ); heap_register_element( compile->heap, &compile->base ); return( compile ); } /* Make a exprinfo suitable for a local. */ Compile * compile_new_local( Expr *expr ) { Compile *compile = compile_new( expr ); compile->heap = heap_new( compile, compile_heap_max_fn, 100, 100 ); g_object_ref( G_OBJECT( compile->heap ) ); iobject_sink( IOBJECT( compile->heap ) ); heap_register_element( compile->heap, &compile->base ); return( compile ); } /* Code generation. */ /* Generate a binop. Point arg1 and arg2 at the elements to be filled in: * caller sets them later. First arg is the compile that this operator came * from. */ static gboolean compile_binop( Compile *compile, BinOp bop, PElement *arg1, PElement *arg2, PElement *out ) { Heap *heap = compile->heap; HeapNode *hn1, *hn2, *hn3; PElement e1, e2; if( NEWNODE( heap, hn1 ) ) return( FALSE ); hn1->type = TAG_APPL; PPUT( hn1, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( out, ELEMENT_NODE, hn1 ); PEPOINTLEFT( hn1, &e1 ); PEPOINTRIGHT( hn1, arg2 ); if( NEWNODE( heap, hn2 ) ) return( FALSE ); hn2->type = TAG_APPL; PPUT( hn2, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( &e1, ELEMENT_NODE, hn2 ); PEPOINTRIGHT( hn2, arg1 ); PEPOINTLEFT( hn2, &e2 ); if( NEWNODE( heap, hn3 ) ) return( FALSE ); hn3->type = TAG_APPL; PPUT( hn3, ELEMENT_BINOP, bop, ELEMENT_COMPILEREF, compile ); PEPUTP( &e2, ELEMENT_NODE, hn3 ); return( TRUE ); } /* Generate "x.sym". Set x to be NULL and point rhs at it .. caller * fills in later. */ static gboolean compile_dotsym( Compile *compile, Symbol *sym, PElement *rhs, PElement *out ) { PElement e; if( !compile_binop( compile, BI_DOT, rhs, &e, out ) ) return( FALSE ); PEPUTP( &e, ELEMENT_SYMREF, sym ); return( TRUE ); } /* Compile a reference to sym from expr. */ static gboolean compile_reference( Compile *compile, Symbol *sym, PElement *out ) { Heap *heap = compile->heap; Compile *parent = compile_get_parent( compile ); #ifdef DEBUG printf( "generate_reference: ref to " ); symbol_name_print( sym ); printf( "inside " ); compile_name_print( compile ); printf( "\n" ); #endif /*DEBUG*/ if( g_slist_find( compile->param, sym ) || g_slist_find( compile->secret, sym ) ) { /* sym is a simple parameter, easy! */ PEPUTP( out, ELEMENT_SYMBOL, sym ); } else if( is_class( parent ) && (symbol_get_parent( sym ) == parent->sym || g_slist_find( parent->secret, sym )) ) { Symbol *ths = parent->this; /* sym is a member of the same class as expr, or sym is a * secret to our constructor (in which case it'll be in this * as well) ... generate (.sym this) * * Optimisation: don't generate (.this this) */ if( sym == ths ) { PEPUTP( out, ELEMENT_SYMBOL, ths ); } else { PElement rhs; if( !compile_dotsym( compile, sym, &rhs, out ) ) return( FALSE ); PEPUTP( &rhs, ELEMENT_SYMBOL, ths ); } } else if( is_member_enclosing( compile, sym ) ) { Symbol *sths = symbol_get_parent( sym )->expr->compile->this; PElement rhs; /* Sym is a member of an enclosing class ... * generate (.sym ref-to-this-for-that-class) */ if( !compile_dotsym( compile, sym, &rhs, out ) || !compile_reference( compile, sths, &rhs ) ) return( FALSE ); } else { /* some other reference ... generate (sym secret1 .. secretn) * recurse for secrets, since we may have to fetch them from * "this" */ PElement e = *out; PElement f; GSList *l; PEPUTP( &e, ELEMENT_SYMBOL, sym ); /* Build secret args to this sym. */ if( sym->expr && sym->expr->compile ) for( l = sym->expr->compile->secret; l; l = l->next ) { Symbol *arg = SYMBOL( l->data ); HeapNode *hn1; if( NEWNODE( heap, hn1 ) ) return( FALSE ); hn1->type = TAG_APPL; PEPUTLEFT( hn1, &e ); PPUTRIGHT( hn1, ELEMENT_ELIST, NULL ); PEPUTP( &e, ELEMENT_NODE, hn1 ); PEPOINTRIGHT( hn1, &f ); if( !compile_reference( compile, arg, &f ) ) return( FALSE ); } } return( TRUE ); } /* Build a graph with vars still in it. Write result to *out. */ static gboolean compile_graph( Compile *compile, ParseNode *pn, PElement *out ) { Heap *heap = compile->heap; HeapNode *hn1, *hn2, *hn3; PElement e1, e2, e3; GSList *l; switch( pn->type ) { case NODE_APPLY: /* Build apply node. */ if( NEWNODE( heap, hn1 ) ) return( FALSE ); hn1->type = TAG_APPL; PPUT( hn1, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( out, ELEMENT_NODE, hn1 ); /* Make sides. */ PEPOINTLEFT( hn1, &e1 ); PEPOINTRIGHT( hn1, &e2 ); if( !compile_graph( compile, pn->arg1, &e1 ) || !compile_graph( compile, pn->arg2, &e2 ) ) return( FALSE ); break; case NODE_UOP: /* Build apply node. */ if( NEWNODE( heap, hn1 ) ) return( FALSE ); hn1->type = TAG_APPL; PPUT( hn1, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( out, ELEMENT_NODE, hn1 ); PEPOINTLEFT( hn1, &e1 ); if( NEWNODE( heap, hn2 ) ) return( FALSE ); hn2->type = TAG_APPL; PPUT( hn2, ELEMENT_UNOP, pn->uop, ELEMENT_COMPILEREF, compile ); PEPUTP( &e1, ELEMENT_NODE, hn2 ); /* Build arg. */ PEPOINTRIGHT( hn1, &e2 ); if( !compile_graph( compile, pn->arg1, &e2 ) ) return( FALSE ); break; case NODE_BINOP: if( !compile_binop( compile, pn->biop, &e1, &e2, out ) || !compile_graph( compile, pn->arg1, &e1 ) || !compile_graph( compile, pn->arg2, &e2 ) ) return( FALSE ); break; case NODE_COMPOSE: if( NEWNODE( heap, hn1 ) ) return( FALSE ); hn1->type = TAG_APPL; PPUT( hn1, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( out, ELEMENT_NODE, hn1 ); PEPOINTLEFT( hn1, &e1 ); if( NEWNODE( heap, hn2 ) ) return( FALSE ); hn2->type = TAG_APPL; PPUT( hn2, ELEMENT_COMB, COMB_SR, ELEMENT_ELIST, NULL ); PEPUTP( &e1, ELEMENT_NODE, hn2 ); /* Build args. */ PEPOINTRIGHT( hn1, &e2 ); PEPOINTRIGHT( hn2, &e3 ); if( !compile_graph( compile, pn->arg1, &e3 ) || !compile_graph( compile, pn->arg2, &e2 ) ) return( FALSE ); break; case NODE_LEAF: /* A reference to a symbol. */ if( !compile_reference( compile, pn->leaf, out ) ) return( FALSE ); break; case NODE_CLASS: /* Output constructor. */ PEPUTP( out, ELEMENT_CONSTRUCTOR, pn->klass ); break; case NODE_TAG: /* RHS of projection. */ PEPUTP( out, ELEMENT_TAG, pn->tag ); break; case NODE_GENERATOR: /* Build apply nodes. */ if( NEWNODE( heap, hn1 ) ) return( FALSE ); hn1->type = TAG_APPL; PPUT( hn1, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( out, ELEMENT_NODE, hn1 ); PEPOINTLEFT( hn1, &e1 ); if( NEWNODE( heap, hn2 ) ) return( FALSE ); hn2->type = TAG_APPL; PPUT( hn2, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( &e1, ELEMENT_NODE, hn2 ); PEPOINTLEFT( hn2, &e2 ); if( NEWNODE( heap, hn3 ) ) return( FALSE ); hn3->type = TAG_APPL; PPUT( hn3, ELEMENT_COMB, COMB_GEN, ELEMENT_ELIST, NULL ); PEPUTP( &e2, ELEMENT_NODE, hn3 ); /* Build args. */ PEPOINTRIGHT( hn1, &e3 ); PEPOINTRIGHT( hn2, &e2 ); PEPOINTRIGHT( hn3, &e1 ); if( !compile_graph( compile, pn->arg1, &e1 ) ) return( FALSE ); if( pn->arg2 ) if( !compile_graph( compile, pn->arg2, &e2 ) ) return( FALSE ); if( pn->arg3 ) if( !compile_graph( compile, pn->arg3, &e3 ) ) return( FALSE ); break; case NODE_LISTCONST: case NODE_SUPER: /* List of expressions. */ /* Make first RHS ... the end of the list. */ e1 = *out; PEPUTP( &e1, ELEMENT_ELIST, NULL ); /* Build @':' for each element. */ for( l = pn->elist; l; l = l->next ) { ParseNode *arg = (ParseNode *) l->data; /* Build apply nodes. */ if( NEWNODE( heap, hn1 ) ) return( FALSE ); hn1->type = TAG_APPL; PPUT( hn1, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( &e1, ELEMENT_NODE, hn1 ); PEPOINTLEFT( hn1, &e2 ); if( NEWNODE( heap, hn2 ) ) return( FALSE ); hn2->type = TAG_APPL; PPUT( hn2, ELEMENT_ELIST, NULL, ELEMENT_ELIST, NULL ); PEPUTP( &e2, ELEMENT_NODE, hn2 ); PEPOINTLEFT( hn2, &e2 ); if( NEWNODE( heap, hn3 ) ) return( FALSE ); hn3->type = TAG_APPL; PPUT( hn3, ELEMENT_BINOP, BI_CONS, ELEMENT_COMPILEREF, compile ); PEPUTP( &e2, ELEMENT_NODE, hn3 ); /* Build arg. */ PEPOINTRIGHT( hn2, &e3 ); if( !compile_graph( compile, arg, &e3 ) ) return( FALSE ); /* APPL is now our LHS. */ PEPOINTRIGHT( hn1, &e1 ); } break; case NODE_CONST: /* Constant. */ switch( pn->con.type ) { case PARSE_CONST_STR: { Managedstring *managedstring; if( !(managedstring = managedstring_find( reduce_context->heap, pn->con.val.str )) ) return( FALSE ); MANAGED_REF( managedstring ); compile->statics = g_slist_prepend( compile->statics, managedstring ); PEPUTP( out, ELEMENT_MANAGED, managedstring ); } break; case PARSE_CONST_CHAR: PEPUTP( out, ELEMENT_CHAR, pn->con.val.ch ); break; case PARSE_CONST_BOOL: PEPUTP( out, ELEMENT_BOOL, pn->con.val.bol ); break; case PARSE_CONST_ELIST: PEPUTP( out, ELEMENT_ELIST, NULL ); break; case PARSE_CONST_NUM: if( !heap_real_new( heap, pn->con.val.num, out ) ) return( FALSE ); break; case PARSE_CONST_COMPLEX: if( !heap_complex_new( heap, 0, pn->con.val.num, out ) ) return( FALSE ); break; default: g_assert( FALSE ); } break; case NODE_NONE: default: g_assert( FALSE ); } return( TRUE ); } /* Parameter abstraction. */ /* Abstract a symbol from the body of a piece of graph. Set *used if we found * the symbol in this piece of graph ... ie. if our caller should add an * Sx-combinator for us. Update *root with the new piece of graph. */ static int compile_abstract_body( Compile *compile, PElement *root, Symbol *sym, gboolean *used ) { Heap *heap = compile->heap; HeapNode *hn; HeapNode *hn1; PElement e1, e2; gboolean b1, b2; CombinatorType comb; switch( PEGETTYPE( root ) ) { case ELEMENT_NODE: hn = PEGETVAL( root ); switch( hn->type ) { case TAG_APPL: case TAG_CONS: b1 = FALSE; b2 = FALSE; PEPOINTLEFT( hn, &e1 ); PEPOINTRIGHT( hn, &e2 ); if( compile_abstract_body( compile, &e1, sym, &b1 ) || compile_abstract_body( compile, &e2, sym, &b2 ) ) return( -1 ); if( PEISCOMB( &e2 ) && PEGETCOMB( &e2 ) == COMB_I && !b1 && b2 && hn->type == TAG_APPL ) { PEPUTPE( root, &e1 ); *used = TRUE; } else if( b1 || b2 ) { if( b1 && !b2 ) comb = COMB_SL; else if( !b1 && b2 ) comb = COMB_SR; else comb = COMB_S; /* Generate Sx combinator. */ if( NEWNODE( heap, hn1 ) ) return( -1 ); hn1->type = TAG_APPL; PPUTLEFT( hn1, ELEMENT_COMB, comb ); PEPUTRIGHT( hn1, &e1 ); PEPUTP( &e1, ELEMENT_NODE, hn1 ); /* We've used the var too! */ *used = TRUE; } break; case TAG_DOUBLE: case TAG_COMPLEX: case TAG_CLASS: case TAG_GEN: break; case TAG_FILE: case TAG_FREE: default: g_assert( FALSE ); } break; case ELEMENT_SYMBOL: if( SYMBOL( PEGETVAL( root ) ) == sym ) { /* Found an instance! Make an I combinator. */ *used = TRUE; PEPUTP( root, ELEMENT_COMB, COMB_I ); } break; case ELEMENT_CONSTRUCTOR: /* set used .. to stop K being generated for this * class parameter. */ *used = TRUE; break; case ELEMENT_MANAGED: case ELEMENT_CHAR: case ELEMENT_BOOL: case ELEMENT_BINOP: case ELEMENT_UNOP: case ELEMENT_COMB: case ELEMENT_ELIST: case ELEMENT_SYMREF: case ELEMENT_COMPILEREF: case ELEMENT_NOVAL: case ELEMENT_TAG: /* Leave alone. */ break; default: g_assert( FALSE ); } return( 0 ); } /* Abstract a symbol from a graph. As above, but make a K if the symbol is * entirely unused. */ static void * compile_abstract_symbol( Symbol *sym, Compile *compile, PElement *root ) { Heap *heap = compile->heap; gboolean b; #ifdef DEBUG printf( "abstracting " ); symbol_name_print( sym ); printf( "\n" ); #endif /*DEBUG*/ b = FALSE; if( compile_abstract_body( compile, root, sym, &b ) ) return( sym ); if( !b ) { HeapNode *hn1; /* Parameter not used! Need a K. */ if( NEWNODE( heap, hn1 ) ) return( sym ); hn1->type = TAG_APPL; PPUTLEFT( hn1, ELEMENT_COMB, COMB_K ); PEPUTRIGHT( hn1, root ); /* Update root. */ PEPUTP( root, ELEMENT_NODE, hn1 ); } return( NULL ); } /* Common sub-expression elimination. */ #ifdef DEBUG_COMMON static void * compile_node_count_sub( HeapNode *hn, int *n ) { *n += 1; return( NULL ); } static int compile_node_count( HeapNode *hn ) { int n; n = 0; heap_map( hn, (heap_map_fn) compile_node_count_sub, &n, NULL ); return( n ); } /* Accumulate total saved here during walk of this tree. */ static int compile_node_sum; #endif /*DEBUG_COMMON*/ /* A hash code we calculate from a bit of heap. */ typedef gpointer CompileHash; /* Combine two hashes. */ #define COMPILEHASH_ADD( A, B ) \ GUINT_TO_POINTER( GPOINTER_TO_UINT( A ) + GPOINTER_TO_UINT( B ) ) /* An int to a hash. */ #define INT_TO_HASH GUINT_TO_POINTER /* Build one of these during sharing analysis. From node pointers to * hash codes, and from hash codes to a list of matching node pointers. */ typedef struct _CompileShare { Compile *compile; GHashTable *node2hash; GHashTable *hash2nodel; } CompileShare; static gboolean compile_share_destroy_sub( gpointer key, gpointer value, gpointer user_data ) { if( value ) g_slist_free( (GSList *) value ); return( TRUE ); } static void compile_share_destroy( CompileShare *share ) { share->compile = NULL; if( share->node2hash ) { g_hash_table_destroy( share->node2hash ); share->node2hash = NULL; } if( share->hash2nodel ) { g_hash_table_foreach_remove( share->hash2nodel, compile_share_destroy_sub, NULL ); g_hash_table_destroy( share->hash2nodel ); share->hash2nodel = NULL; } } static void compile_share_init( CompileShare *share, Compile *compile ) { share->compile = compile; share->node2hash = g_hash_table_new( NULL, g_direct_equal ); share->hash2nodel = g_hash_table_new( NULL, g_direct_equal ); } /* Remove a heapnode from the share. */ static void * compile_share_remove( HeapNode *hn, CompileShare *share ) { CompileHash hash; if( (hash = g_hash_table_lookup( share->node2hash, hn )) ) { GSList *nodel; if( (nodel = g_hash_table_lookup( share->hash2nodel, hash )) ) { nodel = slist_remove_all( nodel, hn ); g_hash_table_replace( share->hash2nodel, hash, nodel ); } g_hash_table_remove( share->node2hash, hn ); } return( NULL ); } /* Add a new heapnode. */ static void compile_share_add( CompileShare *share, HeapNode *hn, CompileHash hash ) { /* Make sure hash is non-zero (very unlikely). */ if( !hash ) hash = INT_TO_HASH( 1 ); if( !g_hash_table_lookup( share->node2hash, hn ) ) { GSList *nodel; g_hash_table_insert( share->node2hash, hn, hash ); if( (nodel = g_hash_table_lookup( share->hash2nodel, hash )) ) { nodel = g_slist_prepend( nodel, hn ); g_hash_table_replace( share->hash2nodel, hash, nodel ); } else { nodel = g_slist_prepend( NULL, hn ); g_hash_table_insert( share->hash2nodel, hash, nodel ); } } } /* From a HeapNode, find a list of the other heapnodes which hashed to the same * value. */ static GSList * compile_share_lookup( CompileShare *share, HeapNode *hn ) { CompileHash hash; if( (hash = (CompileHash) g_hash_table_lookup( share->node2hash, hn )) ) return( g_hash_table_lookup( share->hash2nodel, (gpointer) hash ) ); return( NULL ); } static CompileHash compile_share_scan_node( CompileShare *share, HeapNode *hn ); static CompileHash compile_share_scan_element( CompileShare *share, PElement *e ) { CompileHash hash; switch( PEGETTYPE( e ) ) { case ELEMENT_NODE: hash = compile_share_scan_node( share, PEGETVAL( e ) ); break; case ELEMENT_SYMBOL: case ELEMENT_SYMREF: case ELEMENT_COMPILEREF: case ELEMENT_CHAR: case ELEMENT_BOOL: case ELEMENT_BINOP: case ELEMENT_UNOP: case ELEMENT_COMB: case ELEMENT_CONSTRUCTOR: hash = INT_TO_HASH( PEGETTYPE( e ) + PEGETVAL( e ) ); break; case ELEMENT_ELIST: hash = INT_TO_HASH( ELEMENT_ELIST ); break; case ELEMENT_TAG: hash = INT_TO_HASH( g_str_hash( PEGETTAG( e ) ) ); break; case ELEMENT_MANAGED: hash = INT_TO_HASH( PEGETMANAGED( e )->hash ); break; case ELEMENT_NOVAL: default: hash = 0; g_assert( 0 ); } return( hash ); } /* Calculate a hash for every node in a tree. We can just recurse and * calculate bottom-up, since we'll never get very deep. If we were scanning * run-time code, we'd need a better scheme. */ static CompileHash compile_share_scan_node( CompileShare *share, HeapNode *hn ) { CompileHash hash; PElement a; hash = INT_TO_HASH( 0 ); switch( hn->type ) { case TAG_CONS: case TAG_GEN: case TAG_CLASS: case TAG_COMPLEX: case TAG_APPL: PEPOINTLEFT( hn, &a ); hash = COMPILEHASH_ADD( hash, compile_share_scan_element( share, &a ) ); PEPOINTRIGHT( hn, &a ); hash = COMPILEHASH_ADD( hash, compile_share_scan_element( share, &a ) ); hash = COMPILEHASH_ADD( hash, INT_TO_HASH( (int) hn->type ) ); break; case TAG_DOUBLE: hash = COMPILEHASH_ADD( hash, INT_TO_HASH( (int) hn->body.num ) ); hash = COMPILEHASH_ADD( hash, INT_TO_HASH( (int) hn->type ) ); break; case TAG_FILE: case TAG_REFERENCE: case TAG_SHARED: case TAG_FREE: default: g_assert( FALSE ); } /* Add to accumulated table. */ compile_share_add( share, hn, hash ); return( hash ); } /* Test two sub-trees for equality. */ static gboolean compile_equal_node( HeapNode *hn1, HeapNode *hn2 ) { /* Test for pointer equality. */ if( hn1 == hn2 ) return( TRUE ); /* Test type tags for equality. */ if( hn1->type != hn2->type ) return( FALSE ); /* If double, test immediately. */ if( hn1->type == TAG_DOUBLE ) { if( hn1->body.num == hn2->body.num ) return( TRUE ); else return( FALSE ); } /* If complex, test immediately. */ if( hn1->type == TAG_COMPLEX ) { if( GETLEFT( hn1 )->body.num == GETLEFT( hn2 )->body.num && GETRIGHT( hn1 )->body.num == GETRIGHT( hn2 )->body.num ) return( TRUE ); else return( FALSE ); } /* If compound type, something is wrong! Only built by reduce. */ g_assert( hn1->type != TAG_CLASS ); /* In two parts, test tags. */ if( GETLT( hn1 ) != GETLT( hn2 ) ) return( FALSE ); if( GETRT( hn1 ) != GETRT( hn2 ) ) return( FALSE ); /* Test non-subtree parts. */ if( GETLT( hn1 ) != ELEMENT_NODE ) if( GETLEFT( hn1 ) != GETLEFT( hn2 ) ) return( FALSE ); if( GETRT( hn1 ) != ELEMENT_NODE ) if( GETRIGHT( hn1 ) != GETRIGHT( hn2 ) ) return( FALSE ); /* If sub-trees, test them. */ if( GETLT( hn1 ) == ELEMENT_NODE ) if( !compile_equal_node( GETLEFT( hn1 ), GETLEFT( hn2 ) ) ) return( FALSE ); if( GETRT( hn1 ) == ELEMENT_NODE ) if( !compile_equal_node( GETRIGHT( hn1 ), GETRIGHT( hn2 ) ) ) return( FALSE ); return( TRUE ); } /* Found two equal sub-expressions. We can change hn1 to just be a reference * to hn2. */ static int compile_transform_reference( Compile *compile, HeapNode *hn1, HeapNode *hn2 ) { #ifdef DEBUG { Heap *heap = compile->heap; char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_node( heap, &buf, hn1, TRUE ); printf( "Found common subexpression: %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG*/ #ifdef DEBUG_COMMON compile_node_sum += compile_node_count( hn1 ); #endif /*DEBUG_COMMON*/ /* Zap nodes to indicate sharing. */ hn1->type = TAG_REFERENCE; PPUTLEFT( hn1, ELEMENT_NODE, hn2 ); PPUTRIGHT( hn1, ELEMENT_NODE, NULL ); return( 0 ); } /* Node other hashes to the same value as our node. Test for equality, and if * they match, turn us into a share point and turn the other node into a ref. */ static void * compile_share_test( HeapNode *other, CompileShare *share, HeapNode *hn ) { if( hn != other && compile_equal_node( hn, other ) ) { heap_map( other, (heap_map_fn) compile_share_remove, share, NULL ); compile_transform_reference( share->compile, other, hn ); } return( NULL ); } /* Scan a chunk of tree top-down, looking for and eliminating common nodes. */ static void compile_share_trim( CompileShare *share, HeapNode *hn ) { PElement a; GSList *nodel; if( (nodel = compile_share_lookup( share, hn )) ) slist_map2( nodel, (SListMap2Fn) compile_share_test, share, hn ); switch( hn->type ) { case TAG_CONS: case TAG_GEN: case TAG_CLASS: case TAG_COMPLEX: case TAG_APPL: PEPOINTLEFT( hn, &a ); if( PEISNODE( &a ) ) compile_share_trim( share, PEGETVAL( &a ) ); PEPOINTRIGHT( hn, &a ); if( PEISNODE( &a ) ) compile_share_trim( share, PEGETVAL( &a ) ); break; case TAG_DOUBLE: case TAG_REFERENCE: break; case TAG_SHARED: case TAG_FREE: case TAG_FILE: default: g_assert( FALSE ); } } static void compile_share_scan( Compile *compile, PElement *a ) { if( PEISNODE( a ) ) { HeapNode *hn = PEGETVAL( a ); CompileShare share; compile_share_init( &share, compile ); compile_share_scan_node( &share, hn ); compile_share_trim( &share, hn ); compile_share_destroy( &share ); } } /* Use this to generate an id for each SHARE node. */ static int compile_share_number = 0; /* If this is a REF node, make sure dest is a SHARE node. */ static void * compile_transform_share( HeapNode *hn, Compile *compile ) { Heap *heap = compile->heap; if( hn->type == TAG_REFERENCE ) { HeapNode *hn1 = GETLEFT( hn ); if( hn1->type != TAG_SHARED ) { HeapNode *hn2; #ifdef DEBUG { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_node( heap, &buf, hn1, TRUE ); printf( "Found shared code: %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG*/ if( NEWNODE( heap, hn2 ) ) return( hn ); *hn2 = *hn1; hn1->type = TAG_SHARED; PPUT( hn1, ELEMENT_NODE, hn2, ELEMENT_CHAR, GUINT_TO_POINTER( compile_share_number ) ); compile_share_number++; if( compile_share_number == MAX_RELOC ) { error_top( _( "Too many shared nodes in " "graph." ) ); error_sub( _( "Raise MAX_RELOC" ) ); return( hn ); } } } return( NULL ); } /* Do common-subexpression elimination. */ static gboolean compile_remove_subexpr( Compile *compile, PElement *root ) { HeapNode *rootn = PEGETVAL( root ); #ifdef DEBUG_COMMON static int compile_node_total = 0; #endif /*DEBUG_COMMON*/ if( PEGETTYPE( root ) != ELEMENT_NODE ) /* Nowt to do. */ return( TRUE ); #ifdef DEBUG_COMMON compile_node_sum = 0; #endif /*DEBUG_COMMON*/ /* Scan for common nodes, replace stuff we remove with REFERENCE * nodes. */ compile_share_scan( compile, root ); /* Now search for destinations of reference nodes and mark all shared * sections. Each shared section is given a number ... saves a lookup * during copy. */ compile_share_number = 0; if( heap_map( rootn, (heap_map_fn) compile_transform_share, compile, NULL ) ) { /* We can't leave the graph half-done, it'll confuse the copier * later. Zap the graph. */ PEPUTP( root, ELEMENT_NOVAL, NULL ); return( FALSE ); } #ifdef DEBUG_COMMON if( compile_node_sum ) { compile_node_total += compile_node_sum; printf( "compile_remove_subexpr: " ); symbol_name_print( compile->sym ); printf( "saved %d nodes (total %d)\n", compile_node_sum, compile_node_total ); } #endif /*DEBUG_COMMON*/ return( TRUE ); } /* Top-level compiler driver. */ /* Compile a symbol into a heap. */ static void * compile_heap( Compile *compile ) { PElement base; /* Don't generate code for parser temps. */ if( compile->sym->placeholder ) return( NULL ); PEPOINTE( &base, &compile->base ); /* Is there an existing function base? GC it away. */ if( PEGETTYPE( &base ) != ELEMENT_NOVAL ) { PEPUTP( &base, ELEMENT_NOVAL, (void *) 2 ); if( !heap_gc( compile->heap ) ) return( compile->sym ); return( NULL ); } #ifdef DEBUG printf( "*** compile_expr: about to compile " ); symbol_name_print( compile->sym ); printf( "\n" ); if( compile->tree ) dump_tree( compile->tree ); #endif /*DEBUG*/ /* Compile function body. Tree can be NULL for classes. */ if( compile->tree ) { if( !compile_graph( compile, compile->tree, &base ) ) return( compile->sym ); } else { PEPUTP( &base, ELEMENT_NOVAL, (void *) 3 ); } #ifdef DEBUG { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( compile->heap, &buf, &base, TRUE ); printf( "before var abstraction, compiled \"%s\" to: %s\n", IOBJECT( compile->sym )->name, vips_buf_all( &buf ) ); } #endif /*DEBUG*/ /* Abstract real parameters. */ #ifdef DEBUG printf( "abstracting real params ...\n" ); #endif /*DEBUG*/ if( slist_map2_rev( compile->param, (SListMap2Fn) compile_abstract_symbol, compile, &base ) ) return( compile->sym ); /* Abstract secret parameters. */ #ifdef DEBUG printf( "abstracting secret params ...\n" ); #endif /*DEBUG*/ if( slist_map2_rev( compile->secret, (SListMap2Fn) compile_abstract_symbol, compile, &base ) ) return( compile->sym ); /* Remove common sub-expressions. */ if( !compile_remove_subexpr( compile, &base ) ) return( compile->sym ); #ifdef DEBUG_RESULT { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "compiled " ); symbol_name_print( compile->sym ); printf( "to: " ); graph_pelement( compile->heap, &buf, &base, TRUE ); printf( "%s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_RESULT*/ return( NULL ); } static void *compile_object_sub( Compile *compile ); static void * compile_symbol_sub( Symbol *sym ) { Compile *compile; if( sym->expr && (compile = sym->expr->compile) ) if( compile_object_sub( compile ) ) return( sym ); return( NULL ); } static void * compile_object_sub( Compile *compile ) { if( icontainer_map( ICONTAINER( compile ), (icontainer_map_fn) compile_symbol_sub, NULL, NULL ) ) return( compile ); if( compile_heap( compile ) ) return( compile ); return( NULL ); } /* Top-level compile a thing entry point. */ void * compile_object( Compile *compile ) { /* Walk this tree of symbols computing the secret lists. */ secret_build( compile ); /* Compile all definitions from the inside out. */ if( compile_object_sub( compile ) ) return( compile ); return( NULL ); } static void * compile_toolkit_sub( Tool *tool ) { Compile *compile; if( tool->sym && tool->sym->expr && (compile = tool->sym->expr->compile )) /* Only if we have no code. */ if( compile->base.type == ELEMENT_NOVAL ) if( compile_object( compile ) ) return( tool ); return( NULL ); } /* Scan a toolkit and make sure all the symbols have been compiled. */ void * compile_toolkit( Toolkit *kit ) { return( toolkit_map( kit, (tool_map_fn) compile_toolkit_sub, NULL, NULL ) ); } /* Parse support. */ static ParseNode * compile_check_i18n( Compile *compile, ParseNode *pn ) { switch( pn->type ) { case NODE_APPLY: if( pn->arg1->type == NODE_LEAF && strcmp( IOBJECT( pn->arg1->leaf )->name, "_" ) == 0 && pn->arg2->type == NODE_CONST && pn->arg2->con.type == PARSE_CONST_STR ) { const char *text = pn->arg2->con.val.str; if( main_option_i18n ) { /* Remove msgid duplicates with this. */ static GHashTable *msgid = NULL; if( !msgid ) msgid = g_hash_table_new( g_str_hash, g_str_equal ); if( !g_hash_table_lookup( msgid, text ) ) { char buf[MAX_STRSIZE]; g_hash_table_insert( msgid, (void *) text, NULL ); my_strecpy( buf, text, TRUE ); printf( "msgid \"%s\"\n", buf ); printf( "msgstr \"\"\n\n" ); } } /* We can gettext these at compile time. Replace the * APPLY node with a fixed-up text string. */ pn->type = NODE_CONST; pn->con.type = PARSE_CONST_STR; pn->con.val.str = im_strdupn( _( text ) ); } break; default: break; } return( NULL ); } static ParseNode * compile_check_more( Compile *compile, ParseNode *pn ) { switch( pn->type ) { case NODE_BINOP: switch( pn->biop ) { case BI_MORE: pn->biop = BI_LESS; SWAPP( pn->arg1, pn->arg2 ); break; case BI_MOREEQ: pn->biop = BI_LESSEQ; SWAPP( pn->arg1, pn->arg2 ); break; default: break; } break; default: break; } return( NULL ); } /* Do end-of-parse checks. Called after every 'A = ...' style definition (not * just top-level syms). Used to do lots of checks, not much left now. */ gboolean compile_check( Compile *compile ) { Symbol *sym = compile->sym; Symbol *parent = symbol_get_parent( sym ); /* Check "check" member. */ if( is_member( sym ) && strcmp( IOBJECT( sym )->name, MEMBER_CHECK ) == 0 ) { if( compile->nparam != 0 ) { error_top( _( "Too many arguments." ) ); error_sub( _( "Member \"%s\" of class " "\"%s\" should have no arguments." ), MEMBER_CHECK, symbol_name( parent ) ); return( FALSE ); } } /* Look for (_ "string constant") and pump it through gettext. We can * do a lot of i18n at compile-time. */ #ifdef DEBUG printf( "compile_check_i18n: " ); compile_name_print( compile ); printf( "\n" ); #endif /*DEBUG*/ (void) tree_map( compile, (tree_map_fn) compile_check_i18n, compile->tree, NULL, NULL ); /* Swap MORE and MOREEQ for LESS and LESSEQ. Reduces the number of * cases for the compiler. */ (void) tree_map( compile, (tree_map_fn) compile_check_more, compile->tree, NULL, NULL ); return( TRUE ); } /* Mark error on all exprs using this compile. */ void compile_error_set( Compile *compile ) { (void) slist_map( compile->exprs, (SListMapFn) expr_error_set, NULL ); } /* Patch a pointer on a patch list. */ static void * compile_patch_pointers_sub( void **pnt, void *nsym, void *osym ) { g_assert( *pnt == osym ); *pnt = nsym; return( NULL ); } /* Patch pointers to old to point to new instead. */ static void compile_patch_pointers( Symbol *nsym, Symbol *osym ) { (void) slist_map2( osym->patch, (SListMap2Fn) compile_patch_pointers_sub, nsym, osym ); } /* Sub fn of below. */ static void * compile_resolve_sub( Compile *pnt, Symbol *sym ) { if( !g_slist_find( sym->parents, pnt ) ) sym->parents = g_slist_prepend( sym->parents, pnt ); return( NULL ); } /* Sub fn 2 of below. */ static void * compile_resolve_sub1( Compile *compile ) { return( symbol_fix_counts( compile->sym ) ); } /* We've found a symbol which is the true definition of an unresolved symbol. * We fiddle references to zombie to refer to sym instead. */ static void compile_resolve( Symbol *sym, Symbol *zombie ) { #ifdef DEBUG_RESOLVE printf( "compile_resolve: resolving zombie " ); symbol_name_print( zombie ); printf( "to symbol " ); symbol_name_print( sym ); printf( "\n" ); #endif /*DEBUG_RESOLVE*/ /* Symbol on outer table. Patch pointers to zombie to point to * sym instead. */ compile_patch_pointers( sym, zombie ); /* Also unresolved in outer scope? */ if( sym->type == SYM_ZOMBIE ) /* We may need to move it again - so add the patch * pointers we have just used to the patch list on * sym. */ (void) slist_map( zombie->patch, (SListMapFn) symbol_patch_add, sym ); /* Add other information the ZOMBIE has picked up. We only * need to make the link one way: the patching will make the * other half for us. */ (void) slist_map( zombie->parents, (SListMapFn) compile_resolve_sub, sym ); /* Make sure the dirty counts are set correctly. We have * changed dep (maybe), so need a fiddle. */ (void) slist_map( zombie->parents, (SListMapFn) compile_resolve_sub1, NULL ); /* No one refers to the zombie now. */ IM_FREEF( g_slist_free, zombie->parents ); IDESTROY( zombie ); } /* Sub-function of below. */ static void * compile_resolve_names_sub( Symbol *sym, Compile *outer ) { const char *name = IOBJECT( sym )->name; Symbol *old; /* Is it the sort of thing we are looking for? ZOMBIEs only, please. */ if( sym->type != SYM_ZOMBIE ) return( NULL ); if( (old = compile_lookup( outer, name )) ) compile_resolve( old, sym ); else { /* Nothing on the outer table of that name. Can just move the * symbol across. */ g_object_ref( G_OBJECT( sym ) ); icontainer_child_remove( ICONTAINER( sym ) ); icontainer_child_add( ICONTAINER( outer ), ICONTAINER( sym ), -1 ); g_object_unref( G_OBJECT( sym ) ); } return( NULL ); } /* End of definition parse: we search the symbol table we have built for this * definition, looking for unresolved names (ZOMBIEs). If we find any, we move * the zombie to the enclosing symbol table, since the name may be * resolved one level up. If we find a symbol on the enclosing table of the * same name, we have to patch pointers to our inner ZOMBIE to point to this * new symbol. Nasty! */ void compile_resolve_names( Compile *inner, Compile *outer ) { (void) icontainer_map( ICONTAINER( inner ), (icontainer_map_fn) compile_resolve_names_sub, outer, NULL ); } /* Hit a top-level zombie during reduction. Search outwards to root looking on * enclosing tables for a match. */ Symbol * compile_resolve_top( Symbol *sym ) { Compile *enclosing; for( enclosing = COMPILE( ICONTAINER( sym )->parent ); enclosing; enclosing = compile_get_parent( enclosing ) ) { Symbol *outer_sym; if( (outer_sym = compile_lookup( enclosing, IOBJECT( sym )->name )) && outer_sym->type != SYM_ZOMBIE ) return( outer_sym ); } return( NULL ); } /* Search outwards for this sym. */ static void * compile_resolve_dynamic_sub( Symbol *sym, Compile *context ) { Compile *tab; if( sym->type != SYM_ZOMBIE ) return( NULL ); for( tab = context; tab; tab = compile_get_parent( tab ) ) { Symbol *def = compile_lookup( tab, IOBJECT( sym )->name ); if( def && def->type != SYM_ZOMBIE ) { /* We've found a non-zombie! Bind and we're done. */ compile_resolve( def, sym ); break; } } return( NULL ); } /* Resolve ZOMBIEs in tab by searching outwards from context. We only move * and patch if we find a match ... otherwise we leave the zombie where is is. * * This is used for dynamic exprs in the tally display: we don't care about * fwd refs, but we want to be able to handle multiple scope contexts. */ void compile_resolve_dynamic( Compile *tab, Compile *context ) { (void) icontainer_map( ICONTAINER( tab ), (icontainer_map_fn) compile_resolve_dynamic_sub, context, NULL ); } Symbol * compile_get_member( Compile *compile, const char *name ) { iContainer *child; if( is_class( compile ) && (child = icontainer_child_lookup( ICONTAINER( compile ), name )) ) return( SYMBOL( child ) ); return( NULL ); } const char * compile_get_member_string( Compile *compile, const char *name ) { Symbol *member; Compile *member_compile; if( (member = compile_get_member( compile, name )) && is_value( member ) && (member_compile = member->expr->compile) && member_compile->tree && member_compile->tree->type == NODE_CONST && member_compile->tree->con.type == PARSE_CONST_STR ) return( member_compile->tree->con.val.str ); return( NULL ); } static void * compile_find_generated_node( Compile *compile, ParseNode *node, GSList **list ) { Symbol *sym = node->leaf; if( node->type == NODE_LEAF && sym->generated && symbol_get_parent( sym ) && symbol_get_parent( sym )->expr->compile == compile ) *list = g_slist_prepend( *list, sym ); return( NULL ); } /* Search a scrap of tree and build a list of all the lambdas/lcomps/etc. it * generated. */ static GSList * compile_find_generated( Compile *compile, ParseNode *tree ) { GSList *list; list = NULL; tree_map( compile, (tree_map_fn) compile_find_generated_node, tree, &list, NULL ); return( list ); } /* Make a copy of sym (and all it's children and trees) in the destination * scope. This only works for stuff from the parse stage. Symbols which have * values and stuff attached are too complicated to copy easily. */ static void * compile_copy_sym( Symbol *sym, Compile *dest ) { const char *name = IOBJECT( sym )->name; Symbol *copy_sym; #ifdef DEBUG printf( "compile_copy_sym: copying " ); symbol_name_print( sym ); printf( "to scope of " ); compile_name_print( dest ); printf( "\n" ); #endif /*DEBUG*/ /* Must be a different place. */ g_assert( symbol_get_parent( sym )->expr->compile != dest ); /* Must not be an existing sym of that name. Or if there is, it has to * be a zombie. */ g_assert( !compile_lookup( dest, name ) || compile_lookup( dest, name )->type == SYM_ZOMBIE ); switch( sym->type ) { case SYM_VALUE: copy_sym = symbol_new_defining( dest, name ); copy_sym->generated = sym->generated; (void) symbol_user_init( copy_sym ); (void) compile_new_local( copy_sym->expr ); /* Copy any locals over. We have to do this before we copy the * tree so that the new tree links to the new syms. */ icontainer_map( ICONTAINER( sym->expr->compile ), (icontainer_map_fn) compile_copy_sym, copy_sym->expr->compile, NULL ); copy_sym->expr->compile->tree = tree_copy( copy_sym->expr->compile, sym->expr->compile->tree ); /* Copying the tree may have made some zombies. Resolve * outwards. */ compile_resolve_names( copy_sym->expr->compile, dest ); break; case SYM_PARAM: copy_sym = symbol_new_defining( dest, name ); copy_sym->generated = sym->generated; symbol_parameter_init( copy_sym ); break; case SYM_ZOMBIE: break; case SYM_WORKSPACE: case SYM_WORKSPACEROOT: case SYM_ROOT: case SYM_EXTERNAL: case SYM_BUILTIN: default: g_assert( 0 ); } return( NULL ); } /* tree is a scrap of graph in fromscope's context. It may have caused the * generation of a number of lambdas, lcomps etc. in fromscope. Make a copy * of the tree in toscope and copy over any generated syms too. fromscope and * toscope can be the same, in which case we can just copy the tree. */ ParseNode * compile_copy_tree( Compile *fromscope, ParseNode *tree, Compile *toscope ) { ParseNode *copy_tree; #ifdef DEBUG printf( "compile_copy_tree: copying tree from " ); compile_name_print( fromscope ); printf( " to " ); compile_name_print( toscope ); printf( "\n" ); #endif /*DEBUG*/ /* A new context? Copy generated syms over. */ if( fromscope != toscope ) { GSList *generated; generated = compile_find_generated( fromscope, tree ); #ifdef DEBUG printf( "with generated children: " ); (void) slist_map( generated, (SListMapFn) dump_tiny, NULL ); printf( "\n" ); #endif /*DEBUG*/ slist_map( generated, (SListMapFn) compile_copy_sym, toscope ); g_slist_free( generated ); } copy_tree = tree_copy( toscope, tree ); /* Copying the tree may have made some zombies. Resolve * outwards. */ compile_resolve_names( toscope, compile_get_parent( toscope ) ); return( copy_tree ); } /* Generate the parse tree for this list comprehension. Example: after parse we have: [(x, y) :: x <- [1..3]; y <- [x..3]; x + y > 3]; ... $$lcomp1 ... { $$lcomp1 = NULL { $$result = (x, y); // elements in left-to-right order // in compile->children x = [1..3] y = [x..3] $$filter1 = x + y > 3 } } and we generate this code: z = $$lcomp1 { $$lcomp1 = foldr $f1 [] [1..3] { $f1 x $sofar = foldr $f2 $sofar [x..3] { $f2 y $sofar = if x + y > 3 then $f3 else $sofar { $f3 = (x, y) : $sofar; } } } } */ /* Find the placeholders generated by the parser. Filters, generators, * patterns and $$result. */ static void * compile_lcomp_find( Symbol *sym, GSList **children ) { if( sym->placeholder ) *children = g_slist_append( *children, sym ); return( NULL ); } static Symbol * compile_lcomp_find_pattern( GSList *children, const char *generator ) { int n; char pattern[256]; GSList *p; if( sscanf( generator, "$$generator%d", &n ) != 1 ) return( NULL ); im_snprintf( pattern, 256, "$$pattern%d", n ); for( p = children; p; p = p->next ) { Symbol *sym = (Symbol *) p->data; if( strcmp( IOBJECT( sym )->name, pattern ) == 0 ) return( sym ); } return( NULL ); } void compile_lcomp( Compile *compile ) { /* Number nested locals with this. Keep numbering global so debugging * nested lcomps is easier. */ static int count = 1; GSList *children; gboolean sofar; Compile *scope; Symbol *result; GSList *p; Symbol *child; char name[256]; ParseNode *n1, *n2, *n3; #ifdef DEBUG_LCOMP printf( "before compile_lcomp:\n" ); dump_compile( compile ); #endif /*DEBUG_LCOMP*/ /* Find all the elements of the lcomp: generators, filters, patterns * and $$result. */ children = NULL; (void) icontainer_map( ICONTAINER( compile ), (icontainer_map_fn) compile_lcomp_find, &children, NULL ); #ifdef DEBUG_LCOMP printf( "list comp " ); compile_name_print( compile ); printf( " has children: " ); (void) slist_map( children, (SListMapFn) dump_tiny, NULL ); printf( "\n" ); #endif /*DEBUG_LCOMP*/ /* As yet no list to build on. */ sofar = FALSE; /* Start by building a tree in this scope. */ scope = compile; /* Not seen the result element yet, but we should. */ result = NULL; /* Now generate code for each element, either a filter or a generator. * If we do a generator, we need to search for the associated pattern * and expand it. */ for( p = children; p; p = p->next ) { Symbol *element = (Symbol *) p->data; /* Just note the result element ... we use it right at the end. */ if( strcmp( "$$result", IOBJECT( element )->name ) == 0 ) { result = element; continue; } /* And only process filter/gen. */ if( !is_prefix( "$$filter", IOBJECT( element )->name ) && !is_prefix( "$$gen", IOBJECT( element )->name ) ) continue; /* Start the next nest in. child is the local we will make for * this scope. */ im_snprintf( name, 256, "$$fn%d", count++ ); child = symbol_new_defining( scope, name ); child->generated = TRUE; (void) symbol_user_init( child ); (void) compile_new_local( child->expr ); if( is_prefix( "$$filter", IOBJECT( element )->name ) ) { /* A filter. */ n1 = compile_copy_tree( compile, element->expr->compile->tree, scope ); n2 = tree_leafsym_new( scope, child ); n3 = tree_leaf_new( scope, "$$sofar" ); n1 = tree_ifelse_new( scope, n1, n2, n3 ); scope->tree = n1; } else if( is_prefix( "$$gen", IOBJECT( element )->name ) ) { Symbol *param1; Symbol *param2; Symbol *pattern; GSList *built_syms; /* A generator. */ param1 = symbol_new_defining( child->expr->compile, IOBJECT( element )->name ); param1->generated = TRUE; symbol_parameter_init( param1 ); param2 = symbol_new_defining( child->expr->compile, "$$sofar" ); param2->generated = TRUE; symbol_parameter_init( param2 ); /* Now expand the pattern: it will access parts of the * $$generator argument. */ pattern = compile_lcomp_find_pattern( children, IOBJECT( element )->name ); g_assert( pattern ); built_syms = compile_pattern_lhs( child->expr->compile, param1, pattern->expr->compile->tree ); g_slist_free( built_syms ); /* Make the "foldr $$fn $sofar expr" tree. */ n1 = tree_leaf_new( scope, "foldr" ); n2 = tree_leafsym_new( scope, child ); n3 = tree_appl_new( scope, n1, n2 ); if( sofar ) n2 = tree_leaf_new( scope, "$$sofar" ); else { ParseConst con; con.type = PARSE_CONST_ELIST; n2 = tree_const_new( scope, con ); } n3 = tree_appl_new( scope, n3, n2 ); n2 = compile_copy_tree( compile, element->expr->compile->tree, scope ); n3 = tree_appl_new( scope, n3, n2 ); scope->tree = n3; /* There's now an enclosing sofar we can use. */ sofar = TRUE; } /* Nest in again. */ scope = child->expr->compile; } /* Copy the code for the final result. */ g_assert( result ); n1 = compile_copy_tree( result->expr->compile, result->expr->compile->tree, scope ); n2 = tree_leaf_new( scope, "$$sofar" ); n3 = tree_binop_new( compile, BI_CONS, n1, n2 ); scope->tree = n3; /* Loop outwards again, closing the scopes we made. */ while( scope != compile ) { /* We know check can't fail on generated code. FIXME ... yuk, maybe compile_lcomp should be failable too */ (void) compile_check( scope ); compile_resolve_names( scope, compile_get_parent( scope ) ); scope = compile_get_parent( scope ); } #ifdef DEBUG_LCOMP printf( "after compile_lcomp:\n" ); dump_compile( compile ); #endif /*DEBUG_LCOMP*/ g_slist_free( children ); } /* Compile a pattern LHS. Generate a sym for each pattern variable, each of * which checks and accesses sym. For example: * * [a] = x; * * compiles to: * * sym = x; * a = if is_list sym && len sym == 1 then sym?0 else error ".."; */ /* Generate code to access element n of a pattern trail. Eg, pattern is * [[[a]]] * the trail will be * 0) LISTCONST 1) LISTCONST 2) LISTCONST 3) LEAF * then access(0) will be * leaf * and access(1) will be * leaf?0 * and access(3) (to get the value for a) will be * leaf?0?0?0 */ static ParseNode * compile_pattern_access( Compile *compile, Symbol *leaf, ParseNode **trail, int n ) { ParseNode *node; ParseNode *left; ParseNode *right; ParseConst c; int i; /* The initial leaf ref we access from. */ node = tree_leafsym_new( compile, leaf ); for( i = 0; i < n; i++ ) switch( trail[i]->type ) { case NODE_CONST: case NODE_PATTERN_CLASS: case NODE_LEAF: break; case NODE_BINOP: switch( trail[i]->biop ) { case BI_COMMA: /* Generate re or im? */ if( trail[i]->arg1 == trail[i + 1] ) left = tree_leaf_new( compile, "re" ); else left = tree_leaf_new( compile, "im" ); node = tree_appl_new( compile, left, node ); break; case BI_CONS: /* Generate hd or tl? */ if( trail[i]->arg1 == trail[i + 1] ) left = tree_leaf_new( compile, "hd" ); else left = tree_leaf_new( compile, "tl" ); node = tree_appl_new( compile, left, node ); break; default: g_assert( 0 ); } break; case NODE_LISTCONST: /* Which list element do we need? Look for the next * item in the trail in the list of elements. */ c.type = PARSE_CONST_NUM; c.val.num = g_slist_index( trail[i]->elist, trail[i + 1] ); right = tree_const_new( compile, c ); node = tree_binop_new( compile, BI_SELECT, node, right ); break; default: g_assert( 0 ); } return( node ); } /* Generate a parsetree for the condition test. The array of nodes represents * the set of conditions we have to test, left to right. */ static ParseNode * compile_pattern_condition( Compile *compile, Symbol *leaf, ParseNode **trail, int depth ) { ParseConst n; ParseNode *node; ParseNode *node2; ParseNode *left; ParseNode *right; int i; n.type = PARSE_CONST_BOOL; n.val.bol = TRUE; node = tree_const_new( compile, n ); for( i = depth - 1; i >= 0; i-- ) { switch( trail[i]->type ) { case NODE_LEAF: break; case NODE_BINOP: switch( trail[i]->biop ) { case BI_COMMA: /* Generate is_complex x. */ left = tree_leaf_new( compile, "is_complex" ); right = compile_pattern_access( compile, leaf, trail, i ); node2 = tree_appl_new( compile, left, right ); node = tree_binop_new( compile, BI_LAND, node2, node ); break; case BI_CONS: /* Generate is_list x && x != []. */ left = tree_leaf_new( compile, "is_list" ); right = compile_pattern_access( compile, leaf, trail, i ); node2 = tree_appl_new( compile, left, right ); node = tree_binop_new( compile, BI_LAND, node2, node ); left = compile_pattern_access( compile, leaf, trail, i ); n.type = PARSE_CONST_ELIST; right = tree_const_new( compile, n ); node2 = tree_binop_new( compile, BI_NOTEQ, left, right ); node = tree_binop_new( compile, BI_LAND, node, node2 ); break; default: g_assert( 0 ); } break; case NODE_LISTCONST: /* Generate is_list x && is_list_len n x. */ left = tree_leaf_new( compile, "is_list" ); right = compile_pattern_access( compile, leaf, trail, i ); node2 = tree_appl_new( compile, left, right ); node = tree_binop_new( compile, BI_LAND, node2, node ); left = tree_leaf_new( compile, "is_list_len" ); n.type = PARSE_CONST_NUM; n.val.num = g_slist_length( trail[i]->elist ); right = tree_const_new( compile, n ); left = tree_appl_new( compile, left, right ); right = compile_pattern_access( compile, leaf, trail, i ); node2 = tree_appl_new( compile, left, right ); node = tree_binop_new( compile, BI_LAND, node, node2 ); break; case NODE_CONST: /* Generate x == n. */ left = compile_pattern_access( compile, leaf, trail, i ); right = tree_const_new( compile, trail[i]->con ); node2 = tree_binop_new( compile, BI_EQ, left, right ); node = tree_binop_new( compile, BI_LAND, node2, node ); break; case NODE_PATTERN_CLASS: /* Generate is_instanceof "class-name" x. */ left = tree_leaf_new( compile, "is_instanceof" ); n.type = PARSE_CONST_STR; n.val.str = im_strdupn( trail[i]->tag ); right = tree_const_new( compile, n ); node2 = tree_appl_new( compile, left, right ); right = compile_pattern_access( compile, leaf, trail, i ); node2 = tree_appl_new( compile, node2, right ); node = tree_binop_new( compile, BI_LAND, node2, node ); break; default: g_assert( 0 ); } } return( node ); } /* Generate a parsetree for a "pattern match failed" error. */ static ParseNode * compile_pattern_error( Compile *compile, Symbol *leaf ) { ParseNode *left; ParseConst n; ParseNode *right; ParseNode *node; left = tree_leaf_new( compile, "error" ); n.type = PARSE_CONST_STR; n.val.str = im_strdupn( _( "pattern match failed" ) ); right = tree_const_new( compile, n ); node = tree_appl_new( compile, left, right ); return( node ); } /* Depth of trail we keep as we walk the pattern. */ #define MAX_TRAIL (10) typedef struct _PatternLhs { Compile *compile; /* Scope in which we generate new symbols */ Symbol *sym; /* Thing we access */ /* The trail of nodes representing this slice of the pattern. */ ParseNode *trail[MAX_TRAIL]; int depth; GSList *built_syms; } PatternLhs; /* Generate one reference. leaf is the new sym we generate. */ static void compile_pattern_lhs_leaf( PatternLhs *lhs, Symbol *leaf ) { Symbol *sym; Compile *compile; sym = symbol_new_defining( lhs->compile, IOBJECT( leaf )->name ); sym->generated = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); lhs->built_syms = g_slist_prepend( lhs->built_syms, sym ); compile = sym->expr->compile; compile->tree = tree_ifelse_new( compile, compile_pattern_condition( compile, lhs->sym, lhs->trail, lhs->depth ), compile_pattern_access( compile, lhs->sym, lhs->trail, lhs->depth ), compile_pattern_error( compile, leaf ) ); #ifdef DEBUG_PATTERN printf( "compile_pattern_lhs_leaf: generated\n" ); dump_compile( compile ); #endif /*DEBUG_PATTERN*/ } /* Recurse over the pattern generating references. */ static void * compile_pattern_lhs_sub( ParseNode *node, PatternLhs *lhs ) { lhs->trail[lhs->depth++] = node; switch( node->type ) { case NODE_LEAF: compile_pattern_lhs_leaf( lhs, node->leaf ); break; case NODE_PATTERN_CLASS: compile_pattern_lhs_sub( node->arg1, lhs ); break; case NODE_BINOP: compile_pattern_lhs_sub( node->arg1, lhs ); compile_pattern_lhs_sub( node->arg2, lhs ); break; case NODE_LISTCONST: slist_map( node->elist, (SListMapFn) compile_pattern_lhs_sub, lhs ); break; case NODE_CONST: break; default: g_assert( 0 ); } lhs->depth--; return( NULL ); } /* Something like "[a] = [1];". sym is the $$pattern we are generating access * syms for, node is the pattern tree, compile is the scope in which we * generate the new defining symbols. Return a list of the syms we built: they * will need any final finishing up and then having symbol_made() called on * them. You need to free the list, too. */ GSList * compile_pattern_lhs( Compile *compile, Symbol *sym, ParseNode *node ) { PatternLhs lhs; #ifdef DEBUG_PATTERN printf( "compile_pattern_lhs: building access fns for %s\n", symbol_name( sym ) ); #endif /*DEBUG_PATTERN*/ lhs.compile = compile; lhs.sym = sym; lhs.depth = 0; lhs.built_syms = NULL; compile_pattern_lhs_sub( node, &lhs ); g_assert( lhs.depth == 0 ); return( lhs.built_syms ); } static ParseNode * compile_pattern_has_leaf_sub( Compile *compile, ParseNode *node, void *a, void *b ) { if( node->type == NODE_LEAF ) return( node ); return( NULL ); } /* Does a pattern contain a leaf? We don't allow const-only patterns in * definitions. */ gboolean compile_pattern_has_leaf( ParseNode *node ) { return( tree_map( NULL, (tree_map_fn) compile_pattern_has_leaf_sub, node, NULL, NULL ) != NULL ); } ================================================ FILE: src/compile.h ================================================ /* Stuff to parse and compile text. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Maximum number of shared sections of code in a copy. */ #define MAX_RELOC (1000) #define TYPE_COMPILE (compile_get_type()) #define COMPILE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_COMPILE, Compile )) #define COMPILE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_COMPILE, CompileClass)) #define IS_COMPILE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_COMPILE )) #define IS_COMPILE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_COMPILE )) #define COMPILE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_COMPILE, CompileClass )) /* What we track to parse and compile some text. Our children are our locals. */ struct _Compile { iContainer parent_object; Symbol *sym; /* We are part of this symbol, scopewise */ GSList *exprs; /* We are used by these expressions */ gboolean is_klass; /* True if this is a class */ gboolean has_super; /* True if has a super-class */ char *text; /* The original text */ char *prhstext; /* Parameters plus the RHS of the definition */ char *rhstext; /* Just the RHS of the definition */ ParseNode *tree; /* Parse tree we built */ GSList *treefrag; /* List of tree bits for easy freeing */ Symbol *last_sym; /* The last child we added in this context */ int nparam; /* Number of real parameters */ GSList *param; /* Pointers into locals for real params */ int nsecret; /* Number of secret parameters */ GSList *secret; /* Pointers into locals for secret params */ Symbol *this; /* If we are a class, the "this" local */ Symbol *super; /* If we are a class, the "super" local */ GSList *children; /* Symbols which we directly refer to */ Element base; /* Base of compiled code */ Heap *heap; /* Heap containing compiled code */ GSList *statics; /* Static strings we built */ }; typedef struct _CompileClass { iContainerClass parent_class; /* My methods. */ } CompileClass; Compile *compile_get_parent( Compile *compile ); void *compile_name_print( Compile *compile ); void compile_name( Compile *compile, VipsBuf *buf ); typedef void *(*map_compile_fn)( Compile *, void * ); Compile *compile_map_all( Compile *compile, map_compile_fn fn, void *a ); Symbol *compile_lookup( Compile *compile, const char *name ); void compile_link_make( Compile *compile, Symbol *child ); void *compile_link_break( Compile *compile, Symbol *child ); GtkType compile_get_type( void ); void *compile_expr_link_break( Compile *compile, Expr *expr ); void *compile_expr_link_break_rev( Expr *expr, Compile *compile ); void compile_expr_link_make( Compile *compile, Expr *expr ); Compile *compile_new( Expr *expr ); Compile *compile_new_toplevel( Expr *expr ); Compile *compile_new_local( Expr *expr ); void *compile_object( Compile *compile ); void *compile_toolkit( Toolkit *kit ); void compile_error_set( Compile *compile ); gboolean compile_check( Compile *compile ); void compile_resolve_names( Compile *inner, Compile *outer ); Symbol *compile_resolve_top( Symbol *sym ); void compile_resolve_dynamic( Compile *tab, Compile *context ); Symbol *compile_get_member( Compile *compile, const char *name ); const char *compile_get_member_string( Compile *compile, const char *name ); ParseNode *compile_copy_tree( Compile *fromscope, ParseNode *tree, Compile *toscope ); void compile_lcomp( Compile *compile ); GSList *compile_pattern_lhs( Compile *compile, Symbol *sym, ParseNode *node ); gboolean compile_pattern_has_leaf( ParseNode *node ); gboolean compile_pattern_has_args( Compile *compile ); ================================================ FILE: src/conversion.c ================================================ /* Manage display conversion parameters. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Our signals. */ enum { SIG_AREA_CHANGED, /* Area of repaint image has changed */ SIG_IMAGEINFO_CHANGED, /* The imageinfo we hold has been replaced */ SIG_LAST }; static guint conversion_signals[SIG_LAST] = { 0 }; static ModelClass *parent_class = NULL; /* All active conversions. */ static GSList *conversion_all = NULL; static void * conversion_imageinfo_changed( Conversion *conv ) { #ifdef DEBUG g_print( "conversion_imageinfo_changed: " ); iobject_print( IOBJECT( conv ) ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( conv ), conversion_signals[SIG_IMAGEINFO_CHANGED], 0 ); return( NULL ); } static void conversion_area_changed( Conversion *conv, Rect *dirty ) { g_signal_emit( G_OBJECT( conv ), conversion_signals[SIG_AREA_CHANGED], 0, dirty ); } static void conversion_dispose( GObject *gobject ) { Conversion *conv; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_CONVERSION( gobject ) ); conv = CONVERSION( gobject ); #ifdef DEBUG g_print( "conversion_dispose: " ); iobject_print( IOBJECT( conv ) ); #endif /*DEBUG*/ FREESID( conv->changed_sid, conv->ii ); FREESID( conv->area_changed_sid, conv->ii ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void conversion_finalize( GObject *gobject ) { Conversion *conv; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_CONVERSION( gobject ) ); conv = CONVERSION( gobject ); #ifdef DEBUG g_print( "conversion_finalize: " ); iobject_print( IOBJECT( conv ) ); #endif /*DEBUG*/ conversion_all = g_slist_remove( conversion_all, conv ); IM_FREEF( im_region_free, conv->ireg ); IM_FREEF( im_region_free, conv->mreg ); IM_FREEF( im_region_free, conv->reg ); MANAGED_UNREF( conv->repaint_ii ); MANAGED_UNREF( conv->display_ii ); MANAGED_UNREF( conv->visual_ii ); MANAGED_UNREF( conv->ii ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Make the visualisation image ... eg. we im_histplot histograms, and we * log scale fourier images. */ static IMAGE * conversion_make_visualise( Conversion *conv, IMAGE *in ) { IMAGE *out = im_open( "conversion_make_visualise", "p" ); int tconv = !(conv && conv->enabled && !conv->type); /* Histogram type ... plot the histogram. Keep this old hist display * method in case the goffice plotter is not available. */ if( tconv && in->Type == IM_TYPE_HISTOGRAM && (in->Xsize == 1 || in->Ysize == 1) ) { IMAGE *t[3]; 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_RAD ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); if( !t || im_rad2float( in, t ) ) { im_close( out ); return( NULL ); } in = t; } if( im_open_local_array( out, t, 3, "conv-1", "p" ) || im_histnorm( in, t[0] ) || im_histplot( t[0], t[1] ) ) { im_close( out ); return( NULL ); } /* Scale to a sensible size ... aim for a height of 256 * elements. */ if( in->Xsize == 1 && t[1]->Xsize > 256 ) { if( im_subsample( t[1], t[2], t[1]->Xsize / 256, 1 ) ) { im_close( out ); return( NULL ); } } else if( in->Ysize == 1 && t[1]->Ysize > 256 ) { if( im_subsample( t[1], t[2], 1, t[1]->Ysize / 256 ) ) { im_close( out ); return( NULL ); } } else t[2] = t[1]; in = t[2]; } /* IM_TYPE_FOURIER type ... pow/log scale, good for fourier * transforms. */ if( tconv && in->Type == IM_TYPE_FOURIER ) { IMAGE *t[2]; if( im_open_local_array( out, t, 2, "conv-1", "p" ) || im_abs( in, t[0] ) || im_scaleps( t[0], t[1] ) ) { im_close( out ); return( NULL ); } in = t[1]; } if( im_copy( in, out ) ) { im_close( out ); return( NULL ); } return( out ); } /* What we send from the notify callback to the main GUI thread. */ typedef struct _ConversionUpdate { Conversion *conv; IMAGE *im; Rect area; } ConversionUpdate; static gboolean conversion_render_idle_cb( gpointer data ) { ConversionUpdate *update = (ConversionUpdate *) data; Conversion *conv = update->conv; /* Must be a valid conversion, must be for the image that that * conversion is still using for display. */ if( g_slist_find( conversion_all, conv ) && imageinfo_get( FALSE, conv->display_ii ) == update->im ) { #ifdef DEBUG g_print( "conversion_update_dispatch: left = %d, top = %d, " "width = %d, height = %d\n", update->area.left, update->area.top, update->area.width, update->area.height ); #endif /*DEBUG*/ /* We need to invalid the main image too, since those * regions will have black in from the failed first calc. * * im_render() can't do this invalidate for us, * it needs to be done from the main loop. * * commented out, vips_sink_screen() now does this for us. * im_invalidate( conv->mask ); im_invalidate( imageinfo_get( FALSE, conv->display_ii ) ); */ conversion_area_changed( conv, &update->area ); } #ifdef DEBUG else g_print( "conversion_render_idle_cb: skipping dead update\n" ); #endif /*DEBUG*/ g_free( update ); return( FALSE ); } /* Here from the im_render() background thread. */ static void conversion_render_notify_cb( IMAGE *im, Rect *area, void *client ) { ConversionUpdate *update = g_new( ConversionUpdate, 1 ); /* Can't use CONVERSION() in this thread ... the GUI thread will check * this pointer for us when it reads from the queue. */ update->conv = (Conversion *) client; update->im = im; update->area = *area; g_idle_add( conversion_render_idle_cb, update ); } /* How many tiles should we ask for? A bit more than the number needed to * paint the screen. */ static int conversion_get_default_tiles( Conversion *conv ) { GdkScreen *screen = gdk_screen_get_default(); int width = gdk_screen_get_width( screen ) / conv->tile_size; int height = gdk_screen_get_height( screen ) / conv->tile_size; return( 2 * width * height ); } /* Resize to screen coordinates and cache it. */ static IMAGE * conversion_make_display( Conversion *conv, IMAGE *in, IMAGE **mask_out ) { IMAGE *out = im_open( "conversion_display:1", "p" ); if( !out ) return( NULL ); if( conv->mag < 0 ) { /* Ordinary image ... use im_subsample(). FIXME ... look for pyramid TIFFs here */ IMAGE *t = im_open_local( out, "conv:s", "p" ); /* Don't shrink by more than the image size (ie. to less than * 1 pixel). */ int xshrink = IM_MIN( -conv->mag, in->Xsize ); int yshrink = IM_MIN( -conv->mag, in->Ysize ); if( DISPLAY_THUMBNAIL_HQ ) { if( !t || im_shrink( in, t, xshrink, yshrink ) ) { im_close( out ); return( NULL ); } } else { if( !t || im_subsample( in, t, xshrink, yshrink ) ) { im_close( out ); return( NULL ); } } in = t; } /* Zoom, if necessary. */ if( conv->mag > 1 ) { IMAGE *t = im_open_local( out, "conv:z", "p" ); if( !t || im_zoom( in, t, conv->mag, conv->mag ) ) { im_close( out ); return( NULL ); } in = t; } /* Cache it. */ if( conv->synchronous ) { if( im_copy( in, out ) ) { im_close( out ); return( NULL ); } } else { IMAGE *mask = im_open_local( out, "conv:r", "p" ); if( im_render_priority( in, out, mask, conv->tile_size, conv->tile_size, conversion_get_default_tiles( conv ), conv->priority, conversion_render_notify_cb, conv ) ) { im_close( out ); return( NULL ); } if( mask_out ) *mask_out = mask; } return( out ); } /* Track during lintrauc. */ typedef struct { double a, b; IMAGE *in, *out; } LintraInfo; /* Define what we do for each band element type. Non-complex input, uchar * output. */ #define LOOP(IN) { \ IN *p = (IN *) in; \ PEL *q = (PEL *) out; \ \ for( x = 0; x < sz; x++ ) { \ double t; \ \ t = a * p[x] + b; \ \ if( t > 255 ) \ t = 255; \ else if( t < 0 ) \ t = 0; \ \ q[x] = t; \ } \ } /* Complex input, uchar output. */ #define LOOPCMPLX(IN) { \ IN *p = (IN *) in; \ PEL *q = (PEL *) out; \ \ for( x = 0; x < sz; x++ ) { \ double t; \ \ t = a * p[x << 1] + b; \ \ if( t > 255 ) \ t = 255; \ else if( t < 0 ) \ t = 0; \ \ q[x] = t; \ } \ } /* Lintra a buffer, 1 set of scale/offset. */ static int lintrauc_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf ) { double a = inf->a; double b = inf->b; int sz = width * im->Bands; int x; /* Lintra all input 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: LOOP( double ); break; case IM_BANDFMT_COMPLEX: LOOPCMPLX( float ); break; case IM_BANDFMT_DPCOMPLEX: LOOPCMPLX( double ); break; default: g_assert( 0 ); } return( 0 ); } /* im_lintra() that writes uchar (the VIPS one writes float/double). */ static int im_lintrauc( double a, IMAGE *in, double b, IMAGE *out ) { LintraInfo *inf; if( in->Coding != IM_CODING_NONE ) { im_error( "im_lintrauc", _( "not uncoded" ) ); return( -1 ); } if( im_cp_desc( out, in ) ) return( -1 ); out->Bbits = IM_BBITS_BYTE; out->BandFmt = IM_BANDFMT_UCHAR; if( !(inf = IM_NEW( out, LintraInfo )) ) return( -1 ); inf->a = a; inf->b = b; inf->in = in; inf->out = out; if( im_wrapone( in, out, (im_wrapone_fn) lintrauc_gen, in, inf ) ) return( -1 ); return( 0 ); } /* Turn any IMAGE into a 1/3 band IM_BANDFMT_UCHAR ready for gdk_rgb_*(). */ static IMAGE * conversion_make_repaint( Conversion *conv, IMAGE *in ) { IMAGE *out = im_open( "conversion_apply:1", "p" ); /* 7 is sRGB. this is all deprecated and unused now with vips-7.31 and later tag as unused to stop gcc complaints */ struct im_col_display *display __attribute__ ((unused)) = im_col_displays( 7 ); /* Do we do colorimetric type conversions? Look for * interpret-type-toggle. */ int tconv = !(conv && conv->enabled && !conv->type); if( !out ) return( NULL ); /* Special case: if this is a IM_CODING_LABQ and the display control * bar is turned off, we can go straight to RGB for speed. */ if( in->Coding == IM_CODING_LABQ && !(conv && conv->enabled) ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); static void *table = NULL; /* Make sure fast LabQ2disp tables are built. */ if( !table ) table = im_LabQ2disp_build_table( NULL, display ); if( !t || im_LabQ2disp_table( in, t, table ) ) { im_close( out ); return( NULL ); } in = t; } /* Get the bands right. If we have >3, drop down to 3. If we have 2, * drop down to 1. */ if( in->Coding == IM_CODING_NONE ) { if( in->Bands == 2 ) { 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 ) { 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; } } /* Interpret the Type field for colorimetric images. */ if( tconv && 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_RAD ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); if( !t || im_rad2float( in, t ) ) { im_close( out ); return( NULL ); } in = t; } if( in->Coding != IM_CODING_NONE ) { im_close( out ); return( NULL ); } /* One of the colorimetric types? */ if( tconv && in->Bands == 3 && (in->Type == IM_TYPE_LCH || in->Type == IM_TYPE_YXY || in->Type == IM_TYPE_UCS || #if VIPS_MAJOR_VERSION > 7 || VIPS_MINOR_VERSION > 32 /* scRGB colourspace added in 7.32. */ in->Type == VIPS_INTERPRETATION_scRGB || #endif in->Type == IM_TYPE_LAB || in->Type == IM_TYPE_XYZ) ) { IMAGE *t[2]; /* We need to scale/offset before we go to 8 bit to work well * with HDR. */ if( conv && conv->enabled && (conv->scale != 1.0 || conv->offset != 0.0) ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); if( !t || im_lintra( conv->scale, in, conv->offset, t ) ) { im_close( out ); return( NULL ); } in = t; } if( in->Type == IM_TYPE_LCH ) { 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 VIPS_MAJOR_VERSION > 7 || VIPS_MINOR_VERSION > 32 if( in->Type == VIPS_INTERPRETATION_scRGB ) { VipsImage *x; if( vips_scRGB2sRGB( in, &x, NULL ) ) { im_close( out ); return( NULL ); } vips_object_local( out, x ); in = x; } #endif if( in->Type == IM_TYPE_YXY ) { 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->Type == IM_TYPE_UCS ) { 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->Type == IM_TYPE_LAB ) { 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->Type == IM_TYPE_XYZ ) { 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], display ) ) { im_close( out ); return( NULL ); } in = t[1]; } } else { /* Not colorimetric. We can use a special ->uchar lintra for * scale/offset. Don't scale/offset fourier or histogram * images, they are presented above. */ if( conv && conv->enabled && (!tconv || in->Type != IM_TYPE_FOURIER) && (!tconv || in->Type != IM_TYPE_HISTOGRAM) && (conv->scale != 1.0 || conv->offset != 0.0) ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); if( !t || im_lintrauc( conv->scale, in, conv->offset, t ) ) { im_close( out ); return( NULL ); } in = t; } } if( tconv && (in->Type == IM_TYPE_RGB16 || in->Type == IM_TYPE_GREY16) ) { IMAGE *t[1]; if( im_open_local_array( out, t, 1, "conv-1", "p" ) ) { im_close( out ); return( NULL ); } /* im_msb() only works for the int formats. */ if( vips_bandfmt_isint( in->BandFmt ) ) { if( im_msb( in, t[0] ) ) { im_close( out ); return( NULL ); } } else { if( im_lintrauc( 1 / 256.0, in, 0.0, t[0] ) ) { im_close( out ); return( NULL ); } } in = t[0]; } /* 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_clip2fmt( in, t, IM_BANDFMT_UCHAR ) ) { im_close( out ); return( NULL ); } in = t; } /* Falsecolour. Just use the green channel if we're RGB. */ if( conv && conv->enabled && conv->falsecolour ) { IMAGE *t1 = im_open_local( out, "conv:1", "p" ); if( !t1 ) { im_close( out ); return( NULL ); } if( in->Bands == 3 ) { IMAGE *t2 = im_open_local( out, "conv:1", "p" ); if( im_extract_band( in, t2, 1 ) ) { im_close( out ); return( NULL ); } in = t2; } if( im_falsecolour( in, t1 ) ) { im_close( out ); return( NULL ); } in = t1; } if( im_copy( in, out ) ) { im_close( out ); return( NULL ); } return( out ); } /* Controls in the display conversion bar, or the display image have changed. * Remake the repaint image. */ static void conversion_rebuild_repaint( Conversion *conv ) { IMAGE *display_im; IMAGE *new_repaint_im; Imageinfo *new_repaint_ii; REGION *new_ireg; #ifdef DEBUG g_print( "conversion_remake_repaint: %p\n", conv ); #endif /*DEBUG*/ if( conv->display_ii ) display_im = imageinfo_get( FALSE, conv->display_ii ); else display_im = NULL; /* Keep gcc quiet about annoying possible-used-before-set warnings. */ new_repaint_ii = NULL; new_ireg = NULL; /* Make the new stuff first. */ if( display_im ) { if( !(new_repaint_im = conversion_make_repaint( conv, display_im )) ) return; if( !(new_repaint_ii = imageinfo_new( main_imageinfogroup, NULL, new_repaint_im, NULL )) ) { im_close( new_repaint_im ); return; } managed_sub_add( MANAGED( new_repaint_ii ), MANAGED( conv->display_ii ) ); if( !(new_ireg = im_region_create( new_repaint_im )) ) return; } IM_FREEF( im_region_free, conv->ireg ); MANAGED_UNREF( conv->repaint_ii ); if( display_im ) { conv->repaint_ii = new_repaint_ii; MANAGED_REF( conv->repaint_ii ); conv->ireg = new_ireg; } } /* The magnification or the visual image have changed ... remake the * display image. */ static void conversion_rebuild_display( Conversion *conv ) { IMAGE *visual_im; IMAGE *new_display_im; Imageinfo *new_display_ii; IMAGE *mask; REGION *new_mreg; #ifdef DEBUG g_print( "conversion_remake_display: %p\n", conv ); #endif /*DEBUG*/ if( conv->visual_ii ) visual_im = imageinfo_get( FALSE, conv->visual_ii ); else visual_im = NULL; /* Keep gcc quiet about annoying possible-used-before-set warnings. */ new_display_ii = NULL; new_display_im = NULL; new_mreg = NULL; mask = NULL; /* Make the new stuff first. */ if( visual_im ) { if( !(new_display_im = conversion_make_display( conv, visual_im, &mask )) ) return; if( !(new_display_ii = imageinfo_new( main_imageinfogroup, NULL, new_display_im, NULL )) ) { im_close( new_display_im ); return; } managed_sub_add( MANAGED( new_display_ii ), MANAGED( conv->visual_ii ) ); if( mask && !(new_mreg = im_region_create( mask )) ) return; } IM_FREEF( im_region_free, conv->mreg ); MANAGED_UNREF( conv->display_ii ); if( visual_im ) { conv->display_ii = new_display_ii; conv->mask = mask; conv->mreg = new_mreg; MANAGED_REF( conv->display_ii ); conv->canvas.width = new_display_im->Xsize; conv->canvas.height = new_display_im->Ysize; } /* Certainly need a new repaint image. */ conversion_rebuild_repaint( conv ); } /* The underlying ii has changed. Remake the visualisation image. */ static void conversion_rebuild_visual( Conversion *conv ) { IMAGE *im = imageinfo_get( FALSE, conv->ii ); IMAGE *new_visual_im; Imageinfo *new_visual_ii; REGION *new_reg; #ifdef DEBUG g_print( "conversion_rebuild_visual: %p\n", conv ); #endif /*DEBUG*/ /* Keep gcc quiet about annoying possible-used-before-set warnings. */ new_visual_im = NULL; new_visual_ii = NULL; new_reg = NULL; /* Make new visualization image. */ if( im ) { if( !(new_visual_im = conversion_make_visualise( conv, im )) ) return; if( !(new_visual_ii = imageinfo_new( main_imageinfogroup, NULL, new_visual_im, NULL )) ) { im_close( new_visual_im ); return; } managed_sub_add( MANAGED( new_visual_ii ), MANAGED( conv->ii ) ); if( !(new_reg = im_region_create( im )) ) return; } /* Junk old stuff. */ IM_FREEF( im_region_free, conv->reg ); MANAGED_UNREF( conv->visual_ii ); /* Install new stuff. */ if( im ) { conv->visual_ii = new_visual_ii; MANAGED_REF( conv->visual_ii ); conv->image.width = new_visual_im->Xsize; conv->image.height = new_visual_im->Ysize; conv->reg = new_reg; } /* Certainly need a new display. */ conversion_rebuild_display( conv ); } /* Something has changed ... check it out. */ static void conversion_changed( iObject *iobject ) { Conversion *conv = CONVERSION( iobject ); gboolean rebuild_display = FALSE; gboolean rebuild_repaint = FALSE; #ifdef DEBUG g_print( "conversion_changed: %p\n", conv ); #endif /*DEBUG*/ /* Need to remake the display image if mag has changed. */ if( conv->mag != conv->display_mag ) { rebuild_display = TRUE; conv->display_mag = conv->mag; } /* Need to rebuild repaint if display control bar has changed. */ if( conv->changed ) { conv->changed = FALSE; rebuild_repaint = TRUE; } if( rebuild_display ) conversion_rebuild_display( conv ); else if( rebuild_repaint ) conversion_rebuild_repaint( conv ); IOBJECT_CLASS( parent_class )->changed( iobject ); } static void conversion_class_init( ConversionClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = conversion_dispose; gobject_class->finalize = conversion_finalize; iobject_class->changed = conversion_changed; /* Create signals. */ conversion_signals[SIG_AREA_CHANGED] = g_signal_new( "area_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ConversionClass, area_changed ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER ); conversion_signals[SIG_IMAGEINFO_CHANGED] = g_signal_new( "imageinfo_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ConversionClass, imageinfo_changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } static void conversion_init( Conversion *conv ) { static const Rect emptyrect = { 0, 0, 0, 0 }; #ifdef DEBUG g_print( "conversion_init: " ); iobject_print( IOBJECT( conv ) ); #endif /*DEBUG*/ conv->ii = NULL; conv->changed_sid = 0; conv->area_changed_sid = 0; conv->reg = NULL; conv->synchronous = FALSE; conv->priority = 0; conv->visual_ii = NULL; conv->display_ii = NULL; conv->display_mag = 99999999; conv->repaint_ii = NULL; conv->ireg = NULL; conv->mreg = NULL; /* Default tile size ... OK for image display, too big for * thumbnails. */ conv->tile_size = 64; conv->underlay = emptyrect; conv->image = emptyrect; conv->canvas = emptyrect; conv->visible = emptyrect; conv->mag = 1; conv->changed = FALSE; conv->enabled = FALSE; conv->scale = 1.0; conv->offset = 0.0; conv->falsecolour = FALSE; conv->type = TRUE; conversion_all = g_slist_prepend( conversion_all, conv ); } GType conversion_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ConversionClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) conversion_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Conversion ), 32, /* n_preallocs */ (GInstanceInitFunc) conversion_init, }; type = g_type_register_static( TYPE_MODEL, "Conversion", &info, 0 ); } return( type ); } static void conversion_link( Conversion *conv, Imageinfo *ii ) { iobject_set( IOBJECT( conv ), "display_conversion", NULL ); conversion_set_image( conv, ii ); } Conversion * conversion_new( Imageinfo *ii ) { Conversion *conv; conv = CONVERSION( g_object_new( TYPE_CONVERSION, NULL ) ); conversion_link( conv, ii ); return( conv ); } /* Imageinfo has changed signal. The ii is the same, but the image it * represents may have changed from "p" to "r" or whatever. Assume size/etc. * stay the same. */ static void conversion_ii_changed_cb( Imageinfo *ii, Conversion *conv ) { conversion_rebuild_visual( conv ); iobject_changed( IOBJECT( conv ) ); } /* Something like a paint action on the ii. */ static void conversion_ii_area_changed_cb( Imageinfo *imageinfo, Rect *dirty, Conversion *conv ) { Rect repaint; conversion_im_to_disp_rect( conv, dirty, &repaint ); conversion_area_changed( conv, &repaint ); } /* Install a new image. */ void conversion_set_image( Conversion *conv, Imageinfo *ii ) { /* Pointer compare is safe, since we hold a ref to conv->ii and it * can't have been freed. So we can't have a new ii at the same * address as the old ii. */ if( conv->ii != ii ) { IMAGE *im; if( ii ) im = imageinfo_get( FALSE, ii ); else im = NULL; /* Junk old stuff. */ FREESID( conv->changed_sid, conv->ii ); FREESID( conv->area_changed_sid, conv->ii ); MANAGED_UNREF( conv->ii ); conv->image.width = -1; conv->image.height = -1; /* Install new stuff. */ if( ii ) { conv->ii = ii; MANAGED_REF( conv->ii ); conv->changed_sid = g_signal_connect( G_OBJECT( ii ), "changed", G_CALLBACK( conversion_ii_changed_cb ), conv ); conv->area_changed_sid = g_signal_connect( G_OBJECT( ii ), "area_changed", G_CALLBACK( conversion_ii_area_changed_cb ), conv ); } if( im ) { conv->underlay.width = im->Xsize; conv->underlay.height = im->Ysize; } /* Make new visualization image. */ conversion_rebuild_visual( conv ); /* Tell everyone about our new ii. */ conversion_imageinfo_changed( conv ); } iobject_changed( IOBJECT( conv ) ); } double conversion_dmag( int mag ) { g_assert( mag != 0 ); if( mag > 0 ) return( mag ); else return( 1.0 / (-mag) ); } /* Zoom in and zoom out scale factors ... a little tricky with our funny -ve * representation for subsample. */ int conversion_double( int mag ) { g_assert( mag != -1 ); if( mag == -3 ) return( -2 ); else if( mag == -2 ) return( 1 ); else if( mag > 0 ) return( mag * 2 ); else return( mag / 2 ); } int conversion_halve( int mag ) { g_assert( mag != -1 ); if( mag == 1 ) return( -2 ); else if( mag > 1 ) return( mag / 2 ); else return( mag * 2 ); } /* Convert display to image coordinates and back. */ void conversion_disp_to_im( Conversion *conv, int dx, int dy, int *ix, int *iy ) { double fmag = conversion_dmag( conv->mag ); *ix = (int) (dx / fmag); *iy = (int) (dy / fmag); } void conversion_im_to_disp( Conversion *conv, int ix, int iy, int *dx, int *dy ) { double fmag = conversion_dmag( conv->mag ); *dx = (int) (ix * fmag); *dy = (int) (iy * fmag); } /* Same for rects. */ void conversion_disp_to_im_rect( Conversion *conv, Rect *dr, Rect *ir ) { double fmag = conversion_dmag( conv->mag ); int brx, bry; Rect out; out.left = floor( dr->left / fmag ); out.top = floor( dr->top / fmag ); brx = ceil( IM_RECT_RIGHT( dr ) / fmag ); bry = ceil( IM_RECT_BOTTOM( dr ) / fmag ); out.width = brx - out.left; out.height = bry - out.top; *ir = out; } void conversion_im_to_disp_rect( Conversion *conv, Rect *ir, Rect *dr ) { double fmag = conversion_dmag( conv->mag ); int brx, bry; Rect out; out.left = floor( ir->left * fmag ); out.top = floor( ir->top * fmag ); brx = ceil( IM_RECT_RIGHT( ir ) * fmag ); bry = ceil( IM_RECT_BOTTOM( ir ) * fmag ); out.width = brx - out.left; out.height = bry - out.top; *dr = out; } void conversion_set_mag( Conversion *conv, int mag ) { int x, y; /* Mag 0 means scale image to fit window. Use visible hint in * conversion to pick the mag. */ if( mag == 0 ) { float xfac; float yfac; float fac; /* Need to have image and visible set. */ if( conv->visible.width <= 0 || conv->image.width <= 0 || !conv->ii || !conv->ii->im ) return; xfac = (float) conv->visible.width / conv->image.width; yfac = (float) conv->visible.height / conv->image.height; fac = IM_MIN( xfac, yfac ); if( fac >= 1 ) mag = (int) fac; else /* 0.999 means we don't round up on an exact fit. FIXME ... yuk */ mag = -((int) (0.99999999 + 1.0/fac)); #ifdef DEBUG g_print( "conversion_set_mag: shrink to fit:\n" ); g_print( " visible %dx%d, image %dx%d\n", conv->visible.width, conv->visible.height, conv->image.width, conv->image.height ); g_print( " picked mag of %d\n", mag ); #endif /*DEBUG*/ } /* Check for this-mag-will-cause-integer-overflow. Should flag an * error, but we just bail out instead. */ if( mag > 0 && ((double) conv->image.width * mag > (double) INT_MAX / 2 || (double) conv->image.height * mag > (double) INT_MAX / 2) ) return; /* Will this mag result in width/height of <1? If it will, pick a * mag that most nearly gives us width/height 1. */ conv->mag = mag; conversion_im_to_disp( conv, conv->image.width, conv->image.height, &x, &y ); if( x <= 0 || y <= 0 ) { conv->mag = IM_MAX( -conv->image.width, -conv->image.height ); if( conv->mag == -1 ) conv->mag = 1; } if( conv->mag != conv->display_mag ) iobject_changed( IOBJECT( conv ) ); } void conversion_set_synchronous( Conversion *conv, gboolean synchronous ) { if( conv->synchronous != synchronous ) { #ifdef DEBUG printf( "conversion_set_synchronous: %d", synchronous ); iobject_print( IOBJECT( conv ) ); #endif /*DEBUG*/ conv->synchronous = synchronous; if( conv->ii ) iobject_changed( IOBJECT( conv->ii ) ); } } void conversion_set_params( Conversion *conv, gboolean enabled, double scale, double offset, gboolean falsecolour, gboolean type ) { gboolean changed = FALSE; if( conv->enabled != enabled ) changed = TRUE; if( enabled ) if( conv->scale != scale || conv->offset != offset || conv->falsecolour != falsecolour || conv->type != type ) changed = TRUE; if( changed ) { conv->enabled = enabled; conv->scale = scale; conv->offset = offset; conv->falsecolour = falsecolour; conv->type = type; conv->changed = TRUE; iobject_changed( IOBJECT( conv ) ); } } ================================================ FILE: src/conversion.h ================================================ /* Manage display conversion parameters. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_CONVERSION (conversion_get_type()) #define CONVERSION( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_CONVERSION, Conversion )) #define CONVERSION_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_CONVERSION, ConversionClass)) #define IS_CONVERSION( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_CONVERSION )) #define IS_CONVERSION_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_CONVERSION )) #define CONVERSION_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_CONVERSION, ConversionClass )) struct _Conversion { Model parent_class; /* Image. */ Imageinfo *ii; /* Underlying image */ guint changed_sid; /* Watch ii with these two */ guint area_changed_sid; REGION *reg; /* Region for input from underlying image */ gboolean synchronous; /* TRUE to disable BG stuff */ int priority; /* render priority */ Imageinfo *visual_ii; /* Visualisation image ... eg. histplot */ Imageinfo *display_ii; /* Sized and cached */ int display_mag; /* What mag the display_ii is built for */ IMAGE *mask; /* Read display mask from here */ Imageinfo *repaint_ii; /* Colour converted for screen */ REGION *ireg; /* Region for input from repaint image */ REGION *mreg; /* Region for input from repaint mask */ int tile_size; /* Set smaller for thumbnails */ /* Basic geometry. */ Rect underlay; /* Size of underlying image (at 0,0) */ Rect image; /* Size of visualisation image (at 0,0) */ Rect canvas; /* Size of image we display (always at 0,0) */ Rect visible; /* hint ... visible region of display image */ int mag; /* -ve for shrink, +ve for expand */ /* Visualisation controls. If enabled is set, we built the pipeline * using these params. */ gboolean enabled; gboolean changed; /* Trigger a rebuild with these */ double scale; /* Contrast/brightness */ double offset; gboolean falsecolour; /* False colour display on */ gboolean type; /* Interpret type field */ }; typedef struct _ConversionClass { ModelClass parent_class; /* My methods. area_changed we forward the "area" changed signal off the ii we are holding ... in repaint coordinates */ void (*area_changed)( Conversion *, Rect * ); /* The imageinfo has been swapped for a new one. */ void (*imageinfo_changed)( Conversion * ); } ConversionClass; GType conversion_get_type( void ); Conversion *conversion_new( Imageinfo *ii ); void conversion_set_image( Conversion *conv, Imageinfo *ii ); gboolean conversion_refresh_text( Conversion *conv ); double conversion_dmag( int mag ); int conversion_double( int mag ); int conversion_halve( int mag ); void conversion_disp_to_im( Conversion *conv, int dx, int dy, int *ix, int *iy ); void conversion_im_to_disp( Conversion *conv, int ix, int iy, int *dx, int *dy ); void conversion_disp_to_im_rect( Conversion *conv, Rect *dr, Rect *ir ); void conversion_im_to_disp_rect( Conversion *conv, Rect *ir, Rect *dr ); void conversion_set_mag( Conversion *conv, int mag ); void conversion_set_synchronous( Conversion *conv, gboolean synchronous ); void conversion_set_params( Conversion *conv, gboolean enabled, double scale, double offset, gboolean falsecolour, gboolean type ); ================================================ FILE: src/conversionview.c ================================================ /* display an image in a window ... watching an Image model. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GtkEventBoxClass *parent_class = NULL; /* Find max and min of visible area of image. */ static gboolean conversionview_findmaxmin( Imagemodel *imagemodel, double *min, double *max ) { Conversion *conv = imagemodel->conv; Rect a, b; conversion_disp_to_im_rect( conv, &imagemodel->visible, &a ); im_rect_intersectrect( &a, &conv->image, &b ); if( findmaxmin( imageinfo_get( FALSE, conv->ii ), b.left, b.top, b.width, b.height, min, max ) ) { error_top( _( "Unable to find image range." ) ); error_sub( _( "Find image range failed." ) ); error_vips(); return( FALSE ); } return( TRUE ); } static void conversionview_scale_cb( GtkWidget *wid, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; double min, max; progress_begin(); if( !conversionview_findmaxmin( imagemodel, &min, &max ) ) { progress_end(); iwindow_alert( wid, GTK_MESSAGE_ERROR ); return; } progress_end(); if( max - min < 1e-20 ) { error_top( _( "Unable to scale image." ) ); error_sub( _( "Maximum and minimum pixel values are equal." ) ); iwindow_alert( wid, GTK_MESSAGE_ERROR ); return; } imagemodel->scale = 255.0 / (max - min); imagemodel->offset = -(min * imagemodel->scale); iobject_changed( IOBJECT( imagemodel ) ); } static void conversionview_falsecolour_cb( GtkWidget *wid, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM( wid ); imagemodel->falsecolour = item->active; iobject_changed( IOBJECT( imagemodel ) ); } static void conversionview_interpret_cb( GtkWidget *wid, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM( wid ); imagemodel->type = item->active; iobject_changed( IOBJECT( imagemodel ) ); } static void conversionview_reset_cb( GtkWidget *wid, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; if( imagemodel->iimage ) { Row *row = HEAPMODEL( imagemodel->iimage )->row; imagemodel->scale = row->ws->scale; imagemodel->offset = row->ws->offset; } else { imagemodel->scale = 1.0; imagemodel->offset = 0.0; } imagemodel->falsecolour = FALSE; imagemodel->type = TRUE; iobject_changed( IOBJECT( imagemodel ) ); } static void conversionview_set_default_cb( GtkWidget *wid, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; if( imagemodel->iimage ) { Row *row = HEAPMODEL( imagemodel->iimage )->row; row->ws->scale = imagemodel->scale; row->ws->offset = imagemodel->offset; } } static void conversionview_hide_cb( GtkWidget *wid, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; imagemodel_set_convert( imagemodel, FALSE ); } static void conversionview_class_init( ConversionviewClass *class ) { parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ } /* Value changed in scale adjustment. */ static void conversionview_scale_change_cb( Tslider *tslider, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; if( imagemodel->scale != tslider->value ) { imagemodel->scale = tslider->value; iobject_changed( IOBJECT( imagemodel ) ); } } /* Value changed in offset adjustment. */ static void conversionview_offset_change_cb( Tslider *tslider, Conversionview *cv ) { Imagemodel *imagemodel = cv->imagemodel; if( imagemodel->offset != tslider->value ) { imagemodel->offset = tslider->value; iobject_changed( IOBJECT( imagemodel ) ); } } static void conversionview_init( Conversionview *cv ) { Popupbutton *popupbutton; GtkWidget *hb; GtkWidget *sep; GtkWidget *pane; cv->imagemodel = NULL; gtk_frame_set_shadow_type( GTK_FRAME( cv ), GTK_SHADOW_OUT ); hb = gtk_hbox_new( FALSE, 2 ); gtk_container_set_border_width( GTK_CONTAINER( hb ), 2 ); gtk_container_add( GTK_CONTAINER( cv ), hb ); /* Build menu. One for each window, as we need to track falsecolour * etc. toggles. Could just have one, and modify pre-popup, but this * is easier. */ pane = menu_build( _( "Convert menu" ) ); menu_add_but( pane, _( "_Scale" ), GTK_SIGNAL_FUNC( conversionview_scale_cb ), cv ); cv->falsecolour = menu_add_tog( pane, _( "_False Color" ), GTK_SIGNAL_FUNC( conversionview_falsecolour_cb ), cv ); cv->type = menu_add_tog( pane, _( "_Interpret" ), GTK_SIGNAL_FUNC( conversionview_interpret_cb ), cv ); menu_add_but( pane, _( "_Reset" ), GTK_SIGNAL_FUNC( conversionview_reset_cb ), cv ); menu_add_but( pane, _( "Set As Workspace _Default" ), GTK_SIGNAL_FUNC( conversionview_set_default_cb ), cv ); menu_add_sep( pane ); menu_add_but( pane, GTK_STOCK_CLOSE, GTK_SIGNAL_FUNC( conversionview_hide_cb ), cv ); popupbutton = popupbutton_new(); popupbutton_set_menu( popupbutton, pane ); gtk_box_pack_start( GTK_BOX( hb ), GTK_WIDGET( popupbutton ), FALSE, FALSE, 0 ); cv->scale = tslider_new(); tslider_set_conversions( cv->scale, tslider_log_value_to_slider, tslider_log_slider_to_value ); cv->scale->from = 0.001; cv->scale->to = 255.0; cv->scale->value = 1.0; cv->scale->svalue = 128; cv->scale->digits = 3; tslider_changed( cv->scale ); gtk_box_pack_start( GTK_BOX( hb ), GTK_WIDGET( cv->scale ), TRUE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( cv->scale ), "changed", GTK_SIGNAL_FUNC( conversionview_scale_change_cb ), cv ); tslider_set_ignore_scroll( cv->scale, FALSE ); sep = gtk_vseparator_new(); gtk_box_pack_start( GTK_BOX( hb ), sep, FALSE, FALSE, 0 ); cv->offset = tslider_new(); cv->offset->from = -128; cv->offset->to = 128; cv->offset->value = 0; cv->offset->svalue = 0; cv->offset->digits = 1; tslider_changed( cv->offset ); gtk_box_pack_start( GTK_BOX( hb ), GTK_WIDGET( cv->offset ), TRUE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( cv->offset ), "changed", GTK_SIGNAL_FUNC( conversionview_offset_change_cb ), cv ); tslider_set_ignore_scroll( cv->offset, FALSE ); gtk_widget_show_all( hb ); } GtkType conversionview_get_type( void ) { static GtkType conversionview_type = 0; if( !conversionview_type ) { static const GtkTypeInfo sinfo = { "Conversionview", sizeof( Conversionview ), sizeof( ConversionviewClass ), (GtkClassInitFunc) conversionview_class_init, (GtkObjectInitFunc) conversionview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; conversionview_type = gtk_type_unique( GTK_TYPE_FRAME, &sinfo ); } return( conversionview_type ); } /* Our conversion has changed ... update. */ static void conversionview_changed_cb( Imagemodel *imagemodel, Conversionview *cv ) { GtkCheckMenuItem *item; widget_visible( GTK_WIDGET( cv ), imagemodel->show_convert ); if( !imagemodel->show_convert ) return; if( cv->scale->value != imagemodel->scale ) { cv->scale->value = imagemodel->scale; tslider_changed( cv->scale ); } if( cv->offset->value != imagemodel->offset ) { cv->offset->value = imagemodel->offset; tslider_changed( cv->offset ); } item = GTK_CHECK_MENU_ITEM( cv->falsecolour ); if( item->active != imagemodel->falsecolour ) gtk_check_menu_item_set_active( item, imagemodel->falsecolour ); item = GTK_CHECK_MENU_ITEM( cv->type ); if( item->active != imagemodel->type ) gtk_check_menu_item_set_active( item, imagemodel->type ); } static void conversionview_link( Conversionview *cv, Imagemodel *imagemodel ) { g_assert( !cv->imagemodel ); cv->imagemodel = imagemodel; g_signal_connect( G_OBJECT( cv->imagemodel ), "changed", G_CALLBACK( conversionview_changed_cb ), cv ); } Conversionview * conversionview_new( Imagemodel *imagemodel ) { Conversionview *cv = gtk_type_new( TYPE_CONVERSIONVIEW ); conversionview_link( cv, imagemodel ); return( cv ); } ================================================ FILE: src/conversionview.h ================================================ /* Decls for conversionview.c ... controls for manipulating a conversion */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_CONVERSIONVIEW (conversionview_get_type()) #define CONVERSIONVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_CONVERSIONVIEW, Conversionview )) #define CONVERSIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_CONVERSIONVIEW, ConversionviewClass )) #define IS_CONVERSIONVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_CONVERSIONVIEW )) #define IS_CONVERSIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_CONVERSIONVIEW )) struct _Conversionview { GtkFrame parent_class; Imagemodel *imagemodel; Tslider *scale; Tslider *offset; GtkWidget *falsecolour; /* Toggle menu items */ GtkWidget *type; }; typedef struct _ConversionviewClass { GtkFrameClass parent_class; /* My methods. */ } ConversionviewClass; GtkType conversionview_get_type( void ); Conversionview *conversionview_new( Imagemodel *imagemodel ); ================================================ FILE: src/defbrowser.c ================================================ /* Defbrowser dialog. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; /* Our columns. */ enum { NAME_COLUMN, /* Kit or tool name */ TOOLTIP_COLUMN, TOOL_POINTER_COLUMN, /* Pointer to tool */ KIT_POINTER_COLUMN, /* Pointer to kit (if no tool) */ N_COLUMNS }; static void defbrowser_destroy( GtkObject *object ) { Defbrowser *defbrowser = DEFBROWSER( object ); UNREF( defbrowser->store ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void defbrowser_rebuild_item3( Defbrowser *defbrowser, const char *name, const char *tip, Tool *tool, Toolkit *kit ) { GtkTreeIter iter; gtk_list_store_append( defbrowser->store, &iter ); gtk_list_store_set( defbrowser->store, &iter, NAME_COLUMN, name, TOOLTIP_COLUMN, tip, TOOL_POINTER_COLUMN, tool, KIT_POINTER_COLUMN, kit, -1 ); } static void * defbrowser_rebuild_item2( Tool *tool, Defbrowser *defbrowser ) { if( tool->toolitem && tool->toolitem->tooltip ) defbrowser_rebuild_item3( defbrowser, IOBJECT( tool )->name, tool->toolitem->tooltip, tool, tool->kit ); else defbrowser_rebuild_item3( defbrowser, IOBJECT( tool )->name, tool->help, tool, tool->kit ); return( NULL ); } static void * defbrowser_rebuild_item( Toolkit *kit, Defbrowser *defbrowser ) { toolkit_map( kit, (tool_map_fn) defbrowser_rebuild_item2, defbrowser, NULL ); return( NULL ); } static void defbrowser_refresh( vObject *vobject ) { Defbrowser *defbrowser = DEFBROWSER( vobject ); #ifdef DEBUG printf( "defbrowser_refresh:\n" ); #endif /*DEBUG*/ gtk_list_store_clear( defbrowser->store ); toolkitgroup_map( defbrowser->program->kitg, (toolkit_map_fn) defbrowser_rebuild_item, defbrowser, NULL ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void defbrowser_class_init( DefbrowserClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = defbrowser_destroy; vobject_class->refresh = defbrowser_refresh; } static void defbrowser_entry_changed_cb( GtkEditable *editable, Defbrowser *defbrowser ) { gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( defbrowser->filter ) ); } static gboolean defbrowser_rebuild_test( Tool *tool, const char *text ) { if( tool->toolitem && tool->toolitem->tooltip ) { if( my_strcasestr( tool->toolitem->tooltip, text ) ) return( TRUE ); } if( my_strcasestr( IOBJECT( tool )->name, text ) ) return( TRUE ); return( FALSE ); } static gboolean defbrowser_visible_func( GtkTreeModel *model, GtkTreeIter *iter, gpointer data ) { Defbrowser *defbrowser = DEFBROWSER( data ); const char *text = gtk_entry_get_text( GTK_ENTRY( defbrowser->entry ) ); Tool *tool; gtk_tree_model_get( model, iter, TOOL_POINTER_COLUMN, &tool, -1 ); if( !tool ) return( FALSE ); return( defbrowser_rebuild_test( tool, text ) ); } static Tool * defbrowser_get_selected( Defbrowser *defbrowser ) { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( defbrowser->tree ) ); GtkTreeIter iter; GtkTreeModel *model; Tool *tool; if( gtk_tree_selection_get_selected( selection, &model, &iter ) ) { gtk_tree_model_get( model, &iter, TOOL_POINTER_COLUMN, &tool, -1 ); return( tool ); } return( NULL ); } static gboolean defbrowser_activate_selected( Defbrowser *defbrowser ) { Tool *tool; if( (tool = defbrowser_get_selected( defbrowser )) ) if( !program_select( defbrowser->program, MODEL( tool ) ) ) return( FALSE ); return( TRUE ); } static void defbrowser_selection_changed_cb( GtkTreeSelection *select, Defbrowser *defbrowser ) { if( !defbrowser_activate_selected( defbrowser ) ) iwindow_alert( GTK_WIDGET( defbrowser ), GTK_MESSAGE_ERROR ); } static void defbrowser_init( Defbrowser *defbrowser ) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *label; GtkWidget *swin; GtkTreeSelection *select; defbrowser->top = gtk_hbox_new( FALSE, 12 ); defbrowser->entry = gtk_entry_new(); gtk_signal_connect( GTK_OBJECT( defbrowser->entry ), "changed", GTK_SIGNAL_FUNC( defbrowser_entry_changed_cb ), defbrowser ); gtk_box_pack_end( GTK_BOX( defbrowser->top ), defbrowser->entry, FALSE, FALSE, 2 ); label = gtk_image_new_from_stock( GTK_STOCK_FIND, GTK_ICON_SIZE_MENU ); gtk_box_pack_end( GTK_BOX( defbrowser->top ), label, FALSE, FALSE, 0 ); gtk_box_pack_start( GTK_BOX( defbrowser ), defbrowser->top, FALSE, FALSE, 2 ); gtk_widget_show_all( defbrowser->top ); defbrowser->store = gtk_list_store_new( N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER ); defbrowser->filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( defbrowser->store ), NULL ); gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER( defbrowser->filter ), defbrowser_visible_func, defbrowser, NULL ); defbrowser->tree = gtk_tree_view_new_with_model( GTK_TREE_MODEL( defbrowser->filter ) ); gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( defbrowser->tree ), TRUE ); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( defbrowser->tree ), FALSE ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Name" ), renderer, "text", NAME_COLUMN, NULL ); gtk_tree_view_append_column( GTK_TREE_VIEW( defbrowser->tree ), column ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Tooltip" ), renderer, "text", TOOLTIP_COLUMN, NULL ); gtk_tree_view_append_column( GTK_TREE_VIEW( defbrowser->tree ), column ); select = gtk_tree_view_get_selection( GTK_TREE_VIEW( defbrowser->tree ) ); gtk_tree_selection_set_mode( select, GTK_SELECTION_SINGLE ); g_signal_connect( G_OBJECT( select ), "changed", G_CALLBACK( defbrowser_selection_changed_cb ), defbrowser ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_container_add( GTK_CONTAINER( swin ), defbrowser->tree ); gtk_box_pack_start( GTK_BOX( defbrowser ), swin, TRUE, TRUE, 2 ); gtk_widget_show_all( swin ); } GtkType defbrowser_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Defbrowser", sizeof( Defbrowser ), sizeof( DefbrowserClass ), (GtkClassInitFunc) defbrowser_class_init, (GtkObjectInitFunc) defbrowser_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VOBJECT, &info ); } return( type ); } void defbrowser_set_program( Defbrowser *defbrowser, Program *program ) { g_assert( !defbrowser->program ); #ifdef DEBUG printf( "defbrowser_set_program:\n" ); #endif /*DEBUG*/ defbrowser->program = program; } Defbrowser * defbrowser_new( void ) { Defbrowser *defbrowser = gtk_type_new( TYPE_DEFBROWSER ); return( defbrowser ); } /* Find the 'natural' width of the browser. */ int defbrowser_get_width( Defbrowser *defbrowser ) { if( defbrowser->top ) return( defbrowser->top->requisition.width ); else return( 200 ); } /* Set the filter string. */ void defbrowser_set_filter( Defbrowser *defbrowser, const char *filter ) { gtk_entry_set_text( GTK_ENTRY( defbrowser->entry ), filter ); } ================================================ FILE: src/defbrowser.h ================================================ /* Def browser */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_DEFBROWSER (defbrowser_get_type()) #define DEFBROWSER( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_DEFBROWSER, Defbrowser )) #define DEFBROWSER_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_DEFBROWSER, DefbrowserClass )) #define IS_DEFBROWSER( obj ) \ (GTK_CHECK_TYPE( (obj), TYPE_DEFBROWSER )) #define IS_DEFBROWSER_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_DEFBROWSER )) typedef struct _Defbrowser { vObject parent_object; Program *program; /* Program whose kits we explore */ GtkListStore *store; /* Model for list view */ GtkTreeModel *filter; /* After filtering with search box */ GtkWidget *tree; /* Displayed tree */ GtkWidget *entry; /* Search widget */ GtkWidget *top; /* hbox for top bar */ } Defbrowser; typedef struct _DefbrowserClass { vObjectClass parent_class; } DefbrowserClass; GtkType defbrowser_get_type( void ); void defbrowser_set_program( Defbrowser *defbrowser, Program *program ); Defbrowser *defbrowser_new( void ); int defbrowser_get_width( Defbrowser *defbrowser ); void defbrowser_set_filter( Defbrowser *defbrowser, const char *filter ); ================================================ FILE: src/doubleclick.c ================================================ /* ip: display VASARI format files. * * doubleclick.c: separate single and double clicks on a widget */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include #include #ifdef HAVE_UNISTD_H #include #endif /*HAVE_UNISTD_H*/ #include #include #include #include #include #include #include "doubleclick.h" #define FREEFI( F, S ) { if( S ) { (void) F( S ); (S) = 0; } } /* For debugging. #define DEBUG */ /* The struct we hold our private stuff inside. */ typedef struct doubleclick_info { GtkWidget *wid; /* Widget we are attached to */ guint click; /* Timer for click determination */ gboolean dsingle; /* Do single click on first click of double */ GdkEvent event; /* Copy of last event for clients */ DoubleclickFunc single; /* Callback for single click */ void *clients; /* Client data for single */ DoubleclickFunc dub; /* Callback for double click */ void *clientd; /* Client data for double */ } Doubleclick; /* Allocate and free clicks. */ static Doubleclick * doubleclick_new() { Doubleclick *click; if( !(click = IM_NEW( NULL, Doubleclick )) ) return( NULL ); click->wid = NULL; click->click = 0; click->dsingle = FALSE; click->single = NULL; click->dub = NULL; return( click ); } void doubleclick_free( Doubleclick *click ) { im_free( click ); } /* Timer callback for multiclick detection. */ static gboolean doubleclick_time_cb( Doubleclick *click ) { #ifdef DEBUG g_message( "doubleclick: timeout" ); #endif /*DEBUG*/ click->click = 0; /* There has been no second click before the timeout: we do a single * click. If st->dsingle is set though, we have already delivered the * single-click event, so don't bother. */ if( !click->dsingle && click->single ) { #ifdef DEBUG g_message( "doubleclick: timeout - calling single" ); #endif /*DEBUG*/ click->single( click->wid, &click->event, click->clients ); } /* Stop timer. */ return( FALSE ); } /* There has been an event. Is it single or double? */ static gboolean doubleclick_trigger_cb( GtkWidget *wid, GdkEvent *ev, Doubleclick *click ) { gboolean handled = FALSE; /* Make sure we have a button 1 press. */ if( ev->type != GDK_BUTTON_PRESS || ev->button.button != 1 ) return( handled ); /* Note event for client. */ click->event = *ev; if( click->click ) { /* There is a timeout pending - ie. there was a click * recently. This must be the second part of a double click. * Cancel the timeout and do a double click action. */ FREEFI( g_source_remove, click->click ); if( click->dub ) { #ifdef DEBUG g_message( "doubleclick: seen double" ); #endif /*DEBUG*/ click->dub( click->wid, &click->event, click->clientd ); handled = TRUE; } } else { #ifdef DEBUG g_message( "doubleclick: starting timer" ); #endif /*DEBUG*/ /* No previous click. This may be either. Start a timeout to * help us decide. * * We aren't supposed to look at double_click_time, but * there's no access method, I think. */ click->click = g_timeout_add( gtk_widget_get_display( wid )->double_click_time, (GSourceFunc) doubleclick_time_cb, click ); /* If do-single-on-double is set, we can trigger a * single-click now. */ if( click->dsingle && click->single ) { click->single( click->wid, &click->event, click->clients ); handled = TRUE; } } return( handled ); } /* Destroy a doubleclick_info. */ /*ARGSUSED*/ static void doubleclick_destroy_cb( GtkWidget *wid, Doubleclick *click ) { #ifdef DEBUG g_message( "doubleclick: destroyed" ); #endif /*DEBUG*/ if( click->click ) { /* Don't trigger a single-click, even though there was one * recently, since our widget is being destroyed. */ FREEFI( g_source_remove, click->click ); } doubleclick_free( click ); } static void doubleclick_realize_cb( GtkWidget *wid ) { gtk_widget_add_events( wid, GDK_BUTTON_PRESS_MASK ); } /* Attach callbacks to a widget. */ void doubleclick_add( GtkWidget *wid, gboolean dsingle, DoubleclickFunc single, void *clients, DoubleclickFunc dub, void *clientd ) { Doubleclick *click = doubleclick_new(); /* Complete fields. */ click->wid = wid; click->dsingle = dsingle; click->single = single; click->dub = dub; click->clients = clients; click->clientd = clientd; /* Add callbacks. */ gtk_signal_connect( GTK_OBJECT( wid ), "destroy", GTK_SIGNAL_FUNC( doubleclick_destroy_cb ), click ); gtk_signal_connect( GTK_OBJECT( wid ), "event", GTK_SIGNAL_FUNC( doubleclick_trigger_cb ), click ); gtk_signal_connect( GTK_OBJECT( wid ), "realize", GTK_SIGNAL_FUNC( doubleclick_realize_cb ), NULL ); } ================================================ FILE: src/doubleclick.h ================================================ /* Declarations for doubleclick determiner. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ typedef void (*DoubleclickFunc)( GtkWidget *, GdkEvent *, void * ); #define DOUBLECLICK_FUNC( fn ) ((DoubleclickFunc) (fn)) void doubleclick_add( GtkWidget *wid, gboolean dsingle, DoubleclickFunc single, void *clients, DoubleclickFunc dub, void *clientd ); ================================================ FILE: src/dummy.c ================================================ int poop () {} ================================================ FILE: src/dump.c ================================================ /* Prettyprint various things for debugging. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* A lot of this file is just for debugging. Uncomment to enable all the * debugging code. #define DEBUG */ /* Dump a binary operator. */ char * decode_BinOp( BinOp op ) { switch( op ) { case BI_NONE: return( "(none)" ); case BI_ADD: return( "+" ); case BI_SUB: return( "-" ); case BI_POW: return( "**" ); case BI_REM: return( "%" ); case BI_LSHIFT: return( "<<" ); case BI_RSHIFT: return( ">>" ); case BI_SELECT: return( "?" ); case BI_DIV: return( "/" ); case BI_JOIN: return( "++" ); case BI_COMMA: return( "," ); case BI_DOT: return( "." ); case BI_MUL: return( "*" ); case BI_LAND: return( "&&" ); case BI_LOR: return( "||" ); case BI_BAND: return( "&" ); case BI_BOR: return( "|" ); case BI_EOR: return( "^" ); case BI_EQ: return( "==" ); case BI_NOTEQ: return( "!=" ); case BI_PEQ: return( "===" ); case BI_PNOTEQ: return( "!==" ); case BI_LESS: return( "<" ); case BI_LESSEQ: return( "<=" ); case BI_MORE: return( ">" ); case BI_MOREEQ: return( ">=" ); case BI_IF: return( "if_then_else" ); case BI_CONS: return( ":" ); default: g_assert( FALSE ); /* Keep gcc happy. */ return( NULL ); } } /* Dump a unary operator. */ char * decode_UnOp( UnOp op ) { switch( op ) { case UN_NONE: return( "(none)" ); case UN_CSCHAR: return( "(signed char)" ); case UN_CUCHAR: return( "(unsigned char)" ); case UN_CSSHORT: return( "(signed short)" ); case UN_CUSHORT: return( "(unsigned short)" ); case UN_CSINT: return( "(signed int)" ); case UN_CUINT: return( "(unsigned int)" ); case UN_CFLOAT: return( "(float)" ); case UN_CDOUBLE: return( "(double)" ); case UN_CCOMPLEX: return( "(complex)" ); case UN_CDCOMPLEX: return( "(double complex)" ); case UN_MINUS: return( "-" ); case UN_NEG: return( "!" ); case UN_COMPLEMENT: return( "~" ); case UN_PLUS: return( "+" ); default: g_assert( FALSE ); /* Keep gcc happy. */ return( NULL ); } } /* Decode a node tag. */ char * decode_NodeType( NodeType tag ) { switch( tag ) { case TAG_APPL: return( "TAG_APPL" ); case TAG_CONS: return( "TAG_CONS" ); case TAG_FREE: return( "TAG_FREE" ); case TAG_DOUBLE: return( "TAG_DOUBLE" ); case TAG_COMPLEX: return( "TAG_COMPLEX" ); case TAG_CLASS: return( "TAG_CLASS" ); case TAG_GEN: return( "TAG_GEN" ); case TAG_FILE: return( "TAG_FILE" ); case TAG_SHARED: return( "TAG_SHARED" ); case TAG_REFERENCE: return( "TAG_REFERENCE" ); default: g_assert( FALSE ); /* Keep gcc happy. */ return( NULL ); } } /* Decode a CombinatorType. */ char * decode_CombinatorType( CombinatorType comb ) { switch( comb ) { case COMB_S: return( "S" ); case COMB_SL: return( "Sl" ); case COMB_SR: return( "Sr" ); case COMB_I: return( "I" ); case COMB_K: return( "K" ); case COMB_GEN: return( "G" ); default: g_assert( FALSE ); /* Keep gcc happy. */ return( NULL ); } } /* Decode a symbol type. */ char * decode_SymbolType( SymbolType t ) { switch( t ) { case SYM_VALUE: return( "SYM_VALUE" ); case SYM_PARAM: return( "SYM_PARAM" ); case SYM_ZOMBIE: return( "SYM_ZOMBIE" ); case SYM_WORKSPACE: return( "SYM_WORKSPACE" ); case SYM_WORKSPACEROOT: return( "SYM_WORKSPACEROOT" ); case SYM_ROOT: return( "SYM_ROOT" ); case SYM_EXTERNAL: return( "SYM_EXTERNAL" ); case SYM_BUILTIN: return( "SYM_BUILTIN" ); default: g_assert( FALSE ); return( NULL ); } } /* Decode a symbol type into something users might like to see. */ char * decode_SymbolType_user( SymbolType t ) { switch( t ) { case SYM_VALUE: return( _( "value" ) ); case SYM_PARAM: return( _( "parameter" ) ); case SYM_ZOMBIE: return( _( "zombie" ) ); case SYM_WORKSPACE: return( _( "workspace" ) ); case SYM_WORKSPACEROOT: return( _( "workspace root" ) ); case SYM_ROOT: return( _( "root symbol" ) ); case SYM_EXTERNAL: return( _( "external symbol" ) ); case SYM_BUILTIN: return( _( "built-in symbol" ) ); default: g_assert( FALSE ); return( NULL ); } } /* From here on, just used for debugging. */ #ifdef DEBUG #warning "DEBUG on in dump.c" /* Do a tiny dump of a symbol. Just a few characters. */ void * dump_tiny( Symbol *sym ) { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "(%p) ", sym ); symbol_qualified_name( sym, &buf ); if( sym->dirty ) printf( "*" ); printf( "%s %s; ", decode_SymbolType( sym->type ), vips_buf_all( &buf ) ); return( NULL ); } /* Dump a expr, tiny. */ static void * dump_expr_tiny( Expr *expr ) { printf( "(expr->sym->name = " ); symbol_name_print( expr->sym ); printf( ") " ); return( NULL ); } /* Dump a expr info. */ void dump_expr( Expr *expr ) { Symbol *sym = expr->sym; printf( "expr (%p)->sym->name = \"%s\"\n", expr, IOBJECT( sym )->name ); if( expr->row ) printf( "%s->row = (set)\n", IOBJECT( sym )->name ); if( expr->compile ) { printf( "%s->compile:\n", IOBJECT( sym )->name ); dump_compile( expr->compile ); } if( sym->dirty ) printf( "\n" ); else if( !PEISNOVAL( &expr->root ) ) { printf( "%s->expr->root = ", IOBJECT( sym )->name ); pgraph( &expr->root ); } if( expr->err ) printf( "%s->expr->err = %s\n", IOBJECT( sym )->name, bool_to_char( expr->err ) ); if( expr->error_top ) printf( "%s->expr->error_top = \"%s\"\n", IOBJECT( sym )->name, NN( expr->error_top ) ); if( expr->error_sub ) printf( "%s->expr->error_sub = \"%s\"\n", IOBJECT( sym )->name, NN( expr->error_sub ) ); } /* Dump a compile, tiny. */ static void * dump_compile_tiny( Compile *compile ) { printf( "(compile->sym->name = " ); symbol_name_print( compile->sym ); printf( ") " ); return( NULL ); } /* Dump a compile. */ void dump_compile( Compile *compile ) { Symbol *sym = compile->sym; printf( "compile (%p)->sym->name = \"%s\"\n", compile, IOBJECT( sym )->name ); #ifdef VERBOSE printf( "%s->class = %s\n", IOBJECT( sym )->name, bool_to_char( compile->is_klass ) ); printf( "%s->super = %s\n", IOBJECT( sym )->name, bool_to_char( compile->has_super ) ); printf( "%s->compile->text = \"%s\"\n", IOBJECT( sym )->name, NN( compile->text ) ); printf( "%s->compile->prhstext = \"%s\"\n", IOBJECT( sym )->name, NN( compile->prhstext ) ); printf( "%s->compile->rhstext = \"%s\"\n", IOBJECT( sym )->name, NN( compile->rhstext ) ); #endif /*VERBOSE*/ if( compile->tree ) { printf( "%s->compile->tree = \n", IOBJECT( sym )->name ); (void) dump_tree( compile->tree ); } #ifdef VERBOSE printf( "%s->compile->treefrag = %d pointers\n", IOBJECT( sym )->name, g_slist_length( compile->treefrag ) ); #endif /*VERBOSE*/ if( icontainer_get_n_children( ICONTAINER( compile ) ) > 0 ) { printf( "%s->compile->children =\n", IOBJECT( sym )->name ); (void) icontainer_map( ICONTAINER( compile ), (icontainer_map_fn) dump_symbol, NULL, NULL ); } #ifdef VERBOSE { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "%s->compile->nparam = %d\n", IOBJECT( sym )->name, compile->nparam ); printf( "%s->compile->param = ", IOBJECT( sym )->name ); (void) slist_map( compile->param, (SListMapFn) dump_tiny, NULL ); printf( "\n" ); printf( "%s->compile->nsecret = %d\n", IOBJECT( sym )->name, compile->nsecret ); printf( "%s->compile->secret = ", IOBJECT( sym )->name ); (void) slist_map( compile->secret, (SListMapFn) dump_tiny, NULL ); printf( "\n" ); printf( "%s->compile->this = ", IOBJECT( sym )->name ); if( compile->this ) dump_tiny( compile->this ); else printf( "(null)" ); printf( "\n" ); printf( "%s->compile->super = ", IOBJECT( sym )->name ); if( compile->super ) dump_tiny( compile->super ); else printf( "(null)" ); printf( "\n" ); printf( "%s->compile->children = ", IOBJECT( sym )->name ); (void) slist_map( compile->children, (SListMapFn) dump_tiny, NULL ); printf( "\n" ); graph_element( compile->heap, &buf, &compile->base, FALSE ); printf( "%s->compile->base = %s\n", IOBJECT( sym )->name, vips_buf_all( &buf ) ); if( compile->heap ) iobject_dump( IOBJECT( compile->heap ) ); } #endif /*VERBOSE*/ } /* Print a full symbol and all it's children. */ void * dump_symbol( Symbol *sym ) { printf( "\n\nsym->name = " ); (void) dump_tiny( sym ); printf( "\n" ); #ifdef VERBOSE printf( "%s->patch = %d pointers\n", IOBJECT( sym )->name, g_slist_length( sym->patch ) ); #endif /*VERBOSE*/ if( sym->expr ) dump_expr( sym->expr ); #ifdef VERBOSE printf( "%s->base = ", IOBJECT( sym )->name ); if( !sym->dirty ) { PElement root; PEPOINTE( &root, &sym->base ); pgraph( &root ); } else printf( "" ); printf( "\n" ); printf( "%s->dirty = %s\n", IOBJECT( sym )->name, bool_to_char( sym->dirty ) ); printf( "%s->parents = ", IOBJECT( sym )->name ); (void) slist_map( sym->parents, (SListMapFn) dump_compile_tiny, NULL ); printf( "\n" ); /* Prints topchildren and topparents. */ dump_links( sym ); printf( "%s->ndirtychildren = %d\n", IOBJECT( sym )->name, sym->ndirtychildren ); printf( "%s->leaf = %s\n", IOBJECT( sym )->name, bool_to_char( sym->leaf ) ); printf( "%s->tool = kit ", IOBJECT( sym )->name ); if( sym->tool ) dump_kit( sym->tool->kit ); else printf( "\n" ); #endif /*VERBOSE*/ return( NULL ); } /* Pretty print the whole of the symbol table. */ void dump_symbol_table( void ) { (void) icontainer_map( ICONTAINER( symbol_root->expr->compile ), (icontainer_map_fn) dump_symbol, NULL, NULL ); } /* Tiny dump a tool. */ static void * dump_tiny_tool( Tool *tool ) { switch( tool->type ) { case TOOL_SEP: printf( " " ); break; case TOOL_DIA: printf( " ", FILEMODEL( tool )->filename ); break; case TOOL_SYM: dump_tiny( tool->sym ); break; default: g_assert( FALSE ); } return( NULL ); } /* Print out the syms in a kit. */ void * dump_kit( Toolkit *kit ) { printf( "kit->name = %s; ", IOBJECT( kit )->name ); printf( "%s->tools = ", IOBJECT( kit )->name ); icontainer_map( ICONTAINER( kit ), (icontainer_map_fn) dump_tiny_tool, NULL, NULL ); printf( "\n" ); return( NULL ); } /* Easy find-a-symbol for gdb. */ Symbol * sym( char *name ) { return( compile_lookup( symbol_root->expr->compile, name ) ); } /* Print from a name. */ void psym( char *name ) { Symbol *s; if( (s = sym( name )) ) (void) dump_symbol( s ); else printf( "Symbol \"%s\" not found\n", name ); } /* Print scrap of graph. */ void pgraph( PElement *graph ) { char txt[10240]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( reduce_context->heap, &buf, graph, TRUE ); printf( "%s\n", vips_buf_all( &buf ) ); } /* Print symbol value from name. */ void psymv( char *name ) { Symbol *s; if( (s = sym( name )) ) { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( reduce_context->heap, &buf, &s->expr->root, TRUE ); printf( "%s = %s\n", name, vips_buf_all( &buf ) ); } } /* Pretty-print an element. */ static void print_element( int nsp, EType type, void *arg ) { switch( type ) { case ELEMENT_NOVAL: printf( "no value (%d)\n", GPOINTER_TO_INT( arg ) ); break; case ELEMENT_NODE: printf( "node ->\n" ); graph_heap( nsp + 1, arg ); break; case ELEMENT_SYMBOL: printf( "symbol \"%s\"", IOBJECT( arg )->name ); break; case ELEMENT_SYMREF: printf( "symref \"%s\"", IOBJECT( arg )->name ); break; case ELEMENT_COMPILEREF: printf( "compileref " ); compile_name_print( COMPILE( arg ) ); break; case ELEMENT_CONSTRUCTOR: printf( "constructor \"%s\"", IOBJECT( arg )->name ); break; case ELEMENT_CHAR: printf( "char \"%c\"", GPOINTER_TO_UINT( arg ) ); break; case ELEMENT_BOOL: printf( "bool \"%s\"", bool_to_char( (gboolean) GPOINTER_TO_UINT( arg ) ) ); break; case ELEMENT_BINOP: printf( "binop \"%s\"", decode_BinOp( (BinOp)arg ) ); break; case ELEMENT_UNOP: printf( "unop \"%s\"", decode_UnOp( (UnOp)arg ) ); break; case ELEMENT_COMB: printf( "combinator \"%s\"", decode_CombinatorType( (CombinatorType)arg ) ); break; case ELEMENT_TAG: printf( "tag \"%s\"", (char*)arg ); break; case ELEMENT_MANAGED: printf( "Managed* %p", arg ); break; case ELEMENT_ELIST: printf( "empty-list []" ); break; default: g_assert( FALSE ); } } /* Pretty-print a heap graph. */ void graph_heap( int nsp, HeapNode *hn ) { if( !hn ) return; printf( "%s", spc( nsp ) ); printf( "Node: " ); printf( "serial = %d, ", hn->flgs & FLAG_SERIAL ); if( hn->flgs & FLAG_PRINT ) printf( "print, " ); else printf( "noprint, " ); if( hn->flgs & FLAG_DEBUG ) printf( "debug, " ); else printf( "nodebug, " ); if( hn->flgs & FLAG_MARK ) printf( "marked, " ); else printf( "notmarked, " ); printf( "%s ", decode_NodeType( hn->type ) ); switch( hn->type ) { case TAG_APPL: case TAG_CONS: printf( "\n" ); printf( "%s", spc( nsp ) ); printf( "left: " ); print_element( nsp, hn->ltype, hn->body.ptrs.left ); printf( "\n" ); printf( "%s", spc( nsp ) ); printf( "right: " ); print_element( nsp, hn->rtype, hn->body.ptrs.right ); printf( "\n" ); break; case TAG_DOUBLE: printf( "real \"%g\"\n", hn->body.num ); break; case TAG_CLASS: printf( "instance-of-class \"%s\"\n", IOBJECT( hn->body.ptrs.left )->name ); printf( " secrets=(" ); print_element( nsp, GETRIGHT( hn )->ltype, GETRIGHT( hn )->body.ptrs.left ); printf( ") members=(" ); print_element( nsp, GETRIGHT( hn )->rtype, GETRIGHT( hn )->body.ptrs.right ); printf( ")\n" ); break; case TAG_COMPLEX: printf( "complex \"(%g,%g)\"\n", GETLEFT( hn )->body.num, GETRIGHT( hn )->body.num ); break; case TAG_GEN: printf( "list generator start=%g next=%g final=%g\n", GETLEFT( hn )->body.num, GETLEFT( GETRIGHT( hn ) )->body.num, GETRIGHT( GETRIGHT( hn ) )->body.num ); break; case TAG_FILE: printf( "list generator file=%s\n", MANAGEDFILE( GETLEFT( hn ) )->file->fname ); break; case TAG_FREE: default: g_assert( FALSE ); } } /* Pretty-print a const. */ static void dump_parseconst( ParseConst *pc ) { switch( pc->type ) { case PARSE_CONST_NUM: printf( "%G", pc->val.num ); break; case PARSE_CONST_COMPLEX: printf( "%Gj", pc->val.num ); case PARSE_CONST_STR: printf( "\"%s\"", pc->val.str ); break; case PARSE_CONST_BOOL: printf( "%s", bool_to_char( pc->val.bool ) ); break; case PARSE_CONST_CHAR: printf( "'%c'", pc->val.ch ); break; case PARSE_CONST_ELIST: printf( "[]" ); break; default: g_assert( FALSE ); } } /* Dump a parse tree. */ void * dump_tree( ParseNode *n ) { switch( n->type ) { case NODE_NONE: printf( "node->type == NODE_NONE\n" ); break; case NODE_APPLY: printf( "Function application\n" ); printf( "LHS = " ); (void) dump_tree( n->arg1 ); printf( "RHS = " ); (void) dump_tree( n->arg2 ); break; case NODE_CLASS: printf( "Class: " ); (void) dump_compile_tiny( n->klass ); printf( "\n" ); break; case NODE_LEAF: printf( "Leaf symbol (%p): ", n->leaf ); (void) dump_tiny( n->leaf ); printf( "\n" ); break; case NODE_TAG: printf( "Tag: %s\n", n->tag ); break; case NODE_BINOP: printf( "Binary operator %s\n", decode_BinOp( n->biop ) ); printf( "Left expression:\n" ); (void) dump_tree( n->arg1 ); printf( "Right expression:\n" ); (void) dump_tree( n->arg2 ); break; case NODE_UOP: printf( "Unary operator %s\n", decode_UnOp( n->uop ) ); printf( "Arg expression:\n" ); (void) dump_tree( n->arg1 ); break; case NODE_CONST: printf( "Constant " ); dump_parseconst( &n->con ); printf( "\n" ); break; case NODE_GENERATOR: printf( "List generator\n" ); printf( "Start:\n" ); (void) dump_tree( n->arg1 ); if( n->arg2 ) { printf( "Next:\n" ); (void) dump_tree( n->arg2 ); } if( n->arg3 ) { printf( "End:\n" ); (void) dump_tree( n->arg3 ); } break; case NODE_COMPOSE: printf( "Function compose\n" ); printf( "Left:\n" ); (void) dump_tree( n->arg1 ); printf( "Right:\n" ); (void) dump_tree( n->arg2 ); break; case NODE_LISTCONST: case NODE_SUPER: if( n->type == NODE_LISTCONST ) printf( "List constant\n" ); else printf( "Superclass construct\n" ); printf( "***[\n" ); slist_map_rev( n->elist, (SListMapFn) dump_tree, NULL ); printf( "***]\n" ); break; default: g_assert( FALSE ); } return( NULL ); } static void * dump_link_expr( LinkExpr *le ) { dump_expr_tiny( le->expr ); printf( " count = %d ; ", le->count ); return( NULL ); } void * dump_link( Link *link ) { printf( "link->parent = " ); symbol_name_print( link->parent ); if( link->parent->dirty ) printf( "(dirty)" ); printf( "\n" ); printf( "link->child = " ); symbol_name_print( link->child ); if( link->child->dirty ) printf( "(dirty)" ); printf( "\n" ); printf( "link->serial = %d\n", link->serial ); printf( "link->static_links = " ); slist_map( link->static_links, (SListMapFn) dump_link_expr, NULL ); printf( "\n" ); printf( "link->dynamic_links = " ); slist_map( link->dynamic_links, (SListMapFn) dump_link_expr, NULL ); printf( "\n" ); return( NULL ); } void dump_links( Symbol *sym ) { symbol_name_print( sym ); printf( "->topchildren = \n" ); slist_map( sym->topchildren, (SListMapFn) dump_link, NULL ); symbol_name_print( sym ); printf( "->topparents = \n" ); slist_map( sym->topparents, (SListMapFn) dump_link, NULL ); } void dump_symbol_heap( Symbol *sym ) { printf( "symbol " ); symbol_name_print( sym ); printf( "has graph:\n" ); if( sym->expr ) pgraph( &sym->expr->root ); printf( "\n" ); } #endif /*DEBUG*/ ================================================ FILE: src/dump.h ================================================ /* Decls for dump.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ char *decode_BinOp( BinOp op ); char *decode_UnOp( UnOp op ); char *decode_NodeType( NodeType tag ); char *decode_CombinatorType( CombinatorType comb ); char *decode_SymbolType( SymbolType t ); char *decode_SymbolType_user( SymbolType t ); void *dump_tiny( Symbol *sym ); void *dump_symbol( Symbol *sym ); void dump_expr( Expr *expr ); void dump_compile( Compile *compile ); void dump_symbol_table( void ); void *dump_kit( Toolkit *kit ); Symbol *sym( char *name ); void psym( char *name ); void psymv( char *name ); void pgraph( PElement *graph ); void graph_heap( int nsp, HeapNode *hn ); void graph_test( Heap *heap ); void *dump_tree( ParseNode *n ); void dump_links( Symbol *sym ); void *dump_link( Link *link ); void dump_symbol_heap( Symbol *sym ); ================================================ FILE: src/editview.c ================================================ /* a view of a text thingy */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; static void editview_link( View *view, Model *model, View *parent ) { Editview *editview = EDITVIEW( view ); VIEW_CLASS( parent_class )->link( view, model, parent ); if( GRAPHICVIEW( view )->sview ) gtk_size_group_add_widget( GRAPHICVIEW( view )->sview->group, editview->label ); } static void editview_refresh( vObject *vobject ) { Editview *editview = EDITVIEW( vobject ); #ifdef DEBUG printf( "editview_refresh:\n" ); #endif /*DEBUG*/ if( vobject->iobject->caption ) set_glabel( editview->label, _( "%s:" ), vobject->iobject->caption ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void editview_class_init( EditviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = editview_refresh; view_class->link = editview_link; } /* Detect cancel in a text field. */ static gboolean editview_event_cb( GtkWidget *widget, GdkEvent *ev, Editview *editview ) { gboolean handled; handled = FALSE; if( ev->key.keyval == GDK_Escape ) { handled = TRUE; /* Zap model value back into edit box. */ vobject_refresh_queue( VOBJECT( editview ) ); } return( handled ); } static void editview_activate_cb( GtkWidget *wid, Editview *editview ) { Expr *expr = HEAPMODEL( VOBJECT( editview )->iobject )->row->expr; /* If we've been changed, we'll be on the scannable list ... just * recomp. */ symbol_recalculate_all(); if( expr->err ) { expr_error_get( expr ); iwindow_alert( wid, GTK_MESSAGE_ERROR ); } } static void editview_init( Editview *editview ) { GtkWidget *hbox; gtk_container_set_border_width( GTK_CONTAINER( editview ), 2 ); hbox = gtk_hbox_new( FALSE, 12 ); gtk_box_pack_start( GTK_BOX( editview ), hbox, TRUE, FALSE, 0 ); editview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( editview->label ), 0, 0.5 ); gtk_box_pack_start( GTK_BOX( hbox ), editview->label, FALSE, FALSE, 2 ); editview->text = gtk_entry_new(); gtk_box_pack_start( GTK_BOX( hbox ), editview->text, TRUE, TRUE, 0 ); set_tooltip( editview->text, _( "Escape to cancel edit, " "press Return to accept edit and recalculate" ) ); gtk_signal_connect_object( GTK_OBJECT( editview->text ), "changed", GTK_SIGNAL_FUNC( view_changed_cb ), GTK_OBJECT( editview ) ); gtk_signal_connect( GTK_OBJECT( editview->text ), "activate", GTK_SIGNAL_FUNC( editview_activate_cb ), editview ); gtk_signal_connect( GTK_OBJECT( editview->text ), "event", GTK_SIGNAL_FUNC( editview_event_cb ), editview ); gtk_widget_show_all( hbox ); } GtkType editview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Editview", sizeof( Editview ), sizeof( EditviewClass ), (GtkClassInitFunc) editview_class_init, (GtkObjectInitFunc) editview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( type ); } void editview_set_entry( Editview *editview, const char *fmt, ... ) { va_list ap; char buf[1000]; va_start( ap, fmt ); (void) im_vsnprintf( buf, 1000, fmt, ap ); va_end( ap ); /* Make sure we don't trigger "changed" when we zap in the * text. */ gtk_signal_handler_block_by_data( GTK_OBJECT( editview->text ), editview ); set_gentry( editview->text, "%s", buf ); gtk_signal_handler_unblock_by_data( GTK_OBJECT( editview->text ), editview ); } ================================================ FILE: src/editview.h ================================================ /* abstract base class for text editable view widgets */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_EDITVIEW (editview_get_type()) #define EDITVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_EDITVIEW, Editview )) #define EDITVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_EDITVIEW, EditviewClass )) #define IS_EDITVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_EDITVIEW )) #define IS_EDITVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_EDITVIEW )) typedef struct _Editview { Graphicview parent_object; /* Widgets. */ GtkWidget *label; /* Display caption here */ GtkWidget *text; /* Edit value here */ } Editview; typedef struct _EditviewClass { GraphicviewClass parent_class; /* My methods. */ } EditviewClass; GtkType editview_get_type( void ); void editview_set_entry( Editview *editview, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); ================================================ FILE: src/error.c ================================================ /* ierror window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static LogClass *parent_class = NULL; static void * ierror_print( Expr *expr, iError *ierror, gboolean *found ) { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); expr_error_print( expr, &buf ); log_text( LOG( ierror ), vips_buf_all( &buf ) ); *found = TRUE; return( NULL ); } static void ierror_show_all( iError *ierror ) { gboolean found; found = FALSE; slist_map2( expr_error_all, (SListMap2Fn) ierror_print, ierror, &found ); if( !found ) { log_text( LOG( ierror ), _( "No ierrors found." ) ); log_text( LOG( ierror ), "\n" ); } } static void ierror_show_all_action_cb( GtkAction *action, iError *ierror ) { ierror_show_all( ierror ); } static void * unresolved_print_tool( Tool *tool, iError *ierror, gboolean *found ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); tool_linkreport_tool( tool, &buf, found ); log_text( LOG( ierror ), vips_buf_all( &buf ) ); return( NULL ); } static void * unresolved_print( Toolkit *kit, iError *ierror, gboolean *found ) { toolkit_map( kit, (tool_map_fn) unresolved_print_tool, ierror, found ); return( NULL ); } static void unresolved_show_all( iError *ierror ) { gboolean found; found = FALSE; (void) toolkitgroup_map( ierror->kitg, (toolkit_map_fn) unresolved_print, ierror, &found ); if( !found ) { log_text( LOG( ierror ), _( "No unresolved symbols found." ) ); log_text( LOG( ierror ), "\n" ); } } static void unresolved_show_all_action_cb( GtkAction *action, iError *ierror ) { unresolved_show_all( ierror ); } /* Our actions. */ static GtkActionEntry ierror_actions[] = { { "Clear", NULL, N_( "_Clear" ), NULL, N_( "Clear ierror window" ), G_CALLBACK( log_clear_action_cb ) }, { "iErrors", NULL, N_( "List _iErrors" ), NULL, N_( "Search for all ierrors" ), G_CALLBACK( ierror_show_all_action_cb ) }, { "Unresolved", NULL, N_( "List _Unresolved" ), NULL, N_( "Search for all unresolved references" ), G_CALLBACK( unresolved_show_all_action_cb ) } }; static const char *ierror_menubar_ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static void ierror_class_init( iErrorClass *class ) { LogClass *log_class = (LogClass *) class; parent_class = g_type_class_peek_parent( class ); log_class->actions = ierror_actions; log_class->n_actions = IM_NUMBER( ierror_actions ); log_class->action_name = "iErrorActions"; log_class->ui_description = ierror_menubar_ui_description; log_class->menu_bar_name = "/iErrorMenubar"; } static void ierror_init( iError *ierror ) { } GtkType ierror_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "iError", sizeof( iError ), sizeof( iErrorClass ), (GtkClassInitFunc) ierror_class_init, (GtkObjectInitFunc) ierror_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_LOG, &info ); } return( type ); } static void ierror_link( iError *ierror, Toolkitgroup *kitg ) { ierror->kitg = kitg; destroy_if_destroyed( G_OBJECT( ierror ), G_OBJECT( kitg ), (DestroyFn) gtk_widget_destroy ); iwindow_set_title( IWINDOW( ierror ), _( "iError - %s" ), IOBJECT( kitg )->name ); gtk_window_set_default_size( GTK_WINDOW( ierror ), 640, 480 ); iwindow_set_size_prefs( IWINDOW( ierror ), "IERROR_WIDTH", "IERROR_HEIGHT" ); iwindow_build( IWINDOW( ierror ) ); } iError * ierror_new( Toolkitgroup *kitg ) { iError *ierror = gtk_type_new( TYPE_IERROR ); ierror_link( ierror, kitg ); ierror_show_all( ierror ); unresolved_show_all( ierror ); return( ierror ); } ================================================ FILE: src/error.h ================================================ /* Decls for ierror.c ... show all ierrors */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IERROR (ierror_get_type()) #define IERROR( obj ) (GTK_CHECK_CAST( (obj), TYPE_IERROR, iError )) #define IERROR_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IERROR, iErrorClass )) #define IS_IERROR( obj ) (GTK_CHECK_TYPE( (obj), TYPE_IERROR )) #define IS_IERROR_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IERROR )) struct _iError { Log parent_class; Toolkitgroup *kitg; /* Where we search for link ierrors */ }; typedef struct _iErrorClass { LogClass parent_class; /* My methods. */ } iErrorClass; GtkType ierror_get_type( void ); iError *ierror_new( Toolkitgroup *kitg ); ================================================ FILE: src/expr.c ================================================ /* Expressions! */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Trace error_set()/_clear(). #define DEBUG_ERROR */ /* Trace expr_clone() #define DEBUG_CLONE */ /* #define DEBUG */ #include "ip.h" /* Our signals. */ enum { SIG_NEW_VALUE, /* new value for root */ SIG_LAST }; static iContainerClass *parent_class = NULL; static guint expr_signals[SIG_LAST] = { 0 }; /* Set of expressions containing errors. */ GSList *expr_error_all = NULL; void * expr_error_print( Expr *expr, VipsBuf *buf ) { g_assert( expr->err ); vips_buf_appendf( buf, _( "error in \"%s\"" ), IOBJECT( expr->sym )->name ); if( expr->sym->tool ) tool_error( expr->sym->tool, buf ); else if( expr->row ) { Workspace *ws = expr->row->ws; Workspacegroup *wsg = workspace_get_workspacegroup( ws ); vips_buf_appendf( buf, " (" ); row_qualified_name( expr->row, buf ); if( FILEMODEL( wsg )->filename ) vips_buf_appendf( buf, " - %s", FILEMODEL( wsg )->filename ); vips_buf_appendf( buf, ")" ); } /* Don't show error_top, it's just a summary of error_sub. */ vips_buf_appendf( buf, ": %s\n", expr->error_sub ); return( NULL ); } static Expr * expr_map_all_sub( Symbol *sym, map_expr_fn fn, void *a ) { if( !sym->expr ) return( NULL ); else return( expr_map_all( sym->expr, fn, a ) ); } /* Apply a function to a expr ... and any local exprs. */ Expr * expr_map_all( Expr *expr, map_expr_fn fn, void *a ) { Expr *res; /* Apply to this expr. */ if( (res = fn( expr, a, NULL )) ) return( res ); /* And over any locals. */ if( expr->compile && (res = (Expr *) icontainer_map( ICONTAINER( expr->compile ), (icontainer_map_fn) expr_map_all_sub, (void *) fn, a )) ) return( res ); return( NULL ); } void * expr_name_print( Expr *expr ) { printf( "expr(%p) ", expr ); symbol_name_print( expr->sym ); if( expr->row ) { printf( "(row " ); row_name_print( expr->row ); printf( ") " ); } return( NULL ); } void expr_name( Expr *expr, VipsBuf *buf ) { if( expr->row ) row_qualified_name( expr->row, buf ); else symbol_qualified_name( expr->sym, buf ); } Expr * expr_get_parent( Expr *expr ) { Symbol *sym_parent = symbol_get_parent( expr->sym ); if( !sym_parent ) return( NULL ); return( sym_parent->expr ); } /* Find the enclosing expr in the dynamic scope hierarchy. */ static Expr * expr_get_parent_dynamic( Expr *expr ) { Row *row; if( !expr->row ) return( expr_get_parent( expr ) ); else if( (row = HEAPMODEL( expr->row )->row) ) /* Enclosing row expr. */ return( row->expr ); else { /* Enclosing workspace expr. */ Workspace *ws = expr->row->top_col->ws; return( ws->sym->expr ); } } /* Look back up to find the root expr. */ Expr * expr_get_root( Expr *expr ) { if( is_top( expr->sym ) ) return( expr ); else return( expr_get_root( expr_get_parent( expr ) ) ); } /* Look back up to find the root expr using the dynamic hierarchy (if it's * there). */ Expr * expr_get_root_dynamic( Expr *expr ) { Expr *parent; if( is_top( expr->sym ) ) return( expr ); else if( expr->row && expr->row->top_row && expr->row->top_row->expr ) return( expr->row->top_row->expr ); else if( (parent = expr_get_parent_dynamic( expr )) ) return( expr_get_root_dynamic( parent ) ); else return( NULL ); } /* Is an expr part of a row, including enclosing exprs. * * For example, row A1 could be "[x::x<-A2]", that would be expanded to * something like * "$lcomp0 {$lcomp0 = foldr $f0 [] A2 {$f0 x $sofar = x : $sofar}}" * Now, row A1 depends on A2, but expr A1 will not ... it's $lcomp0, the local * expr of A1, that will get called for expr_dirty. * * Return NULL for expr is not a row and has no enclosing rows. */ static Row * expr_get_row( Expr *expr ) { if( expr->row ) return( expr->row ); else if( is_top( expr->sym ) ) return( NULL ); else return( expr_get_row( expr_get_parent( expr ) ) ); } void expr_new_value( Expr *expr ) { #ifdef DEBUG { PElement *root = &expr->root; printf( "expr_new_value: " ); symbol_name_print( expr->sym ); printf( ": " ); graph_pointer( root ); } #endif /*DEBUG*/ g_signal_emit( G_OBJECT( expr ), expr_signals[SIG_NEW_VALUE], 0 ); } /* An expr has lost a value. */ void expr_value_destroy( Expr *expr ) { /* Break ImageInfo link (if any). */ if( expr->imageinfo ) imageinfo_expr_remove( expr, expr->imageinfo ); } /* Clean up an expr, ready to have a new def parsed into it. */ void * expr_strip( Expr *expr ) { expr_error_clear( expr ); /* Break top links we're part of. */ if( slist_map( expr->static_links, (SListMapFn) link_expr_destroy, NULL ) ) return( expr ); if( slist_map( expr->dynamic_links, (SListMapFn) link_expr_destroy, NULL ) ) return( expr ); g_assert( !expr->static_links ); g_assert( !expr->dynamic_links ); /* Junk error stuff. */ IM_FREE( expr->error_top ); IM_FREE( expr->error_sub ); /* Unref the compile. */ if( expr->compile ) (void) compile_expr_link_break( expr->compile, expr ); return( NULL ); } static void expr_dispose( GObject *gobject ) { Expr *expr = EXPR( gobject ); Symbol *sym = expr->sym; #ifdef DEBUG printf( "expr_dispose: " ); expr_name_print( expr ); printf( "\n" ); #endif /*DEBUG*/ expr_strip( expr ); /* Break the value link. */ expr_value_destroy( expr ); /* Unlink from symbol. */ if( sym->expr == expr ) sym->expr = NULL; if( expr->row ) { Row *row = expr->row; /* If this is the sym for a top row, kill the row too. * Otherwise just break the link and wait for the next row * refresh to do the kill for us. */ if( row == row->top_row ) { IDESTROY( row ); } else { row->expr = NULL; row->sym = NULL; expr->row = NULL; /* Make sure we will re-parse and compile any text * with this sym that might have been modified from * the default. */ if( row->child_rhs && row->child_rhs->itext ) { iText *itext = ITEXT( row->child_rhs->itext ); if( itext->edited ) heapmodel_set_modified( HEAPMODEL( itext ), TRUE ); } } } G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void expr_info( iObject *iobject, VipsBuf *buf ) { Expr *expr = EXPR( iobject ); if( expr->err ) { vips_buf_appends( buf, _( "Error" ) ); vips_buf_appendf( buf, ": %s\n%s\n", expr->error_top, expr->error_sub ); } } static void expr_real_new_value( Expr *expr ) { PElement *root = &expr->root; expr_value_destroy( expr ); if( PEISIMAGE( root ) && PEGETII( root ) ) imageinfo_expr_add( PEGETII( root ), expr ); /* If this is the main expr for this symbol, signal new value there * too. */ if( expr->sym->expr == expr ) symbol_new_value( expr->sym ); } static void expr_class_init( ExprClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ expr_signals[SIG_NEW_VALUE] = g_signal_new( "new_value", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ExprClass, new_value ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); /* Init methods. */ gobject_class->dispose = expr_dispose; iobject_class->info = expr_info; class->new_value = expr_real_new_value; /* Static init. */ } static void expr_init( Expr *expr ) { expr->sym = NULL; expr->row = NULL; expr->compile = NULL; expr->static_links = NULL; expr->dynamic_links = NULL; expr->imageinfo = NULL; expr->err = FALSE; expr->error_top = NULL; expr->error_sub = NULL; } GType expr_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ExprClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) expr_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Expr ), 32, /* n_preallocs */ (GInstanceInitFunc) expr_init, }; type = g_type_register_static( TYPE_ICONTAINER, "Expr", &info, 0 ); } return( type ); } Expr * expr_new( Symbol *sym ) { Expr *expr; expr = EXPR( g_object_new( TYPE_EXPR, NULL ) ); expr->sym = sym; PEPOINTE( &expr->root, &sym->base ); icontainer_child_add( ICONTAINER( sym ), ICONTAINER( expr ), -1 ); #ifdef DEBUG printf( "expr_new: " ); expr_name_print( expr ); printf( "\n" ); #endif /*DEBUG*/ return( expr ); } /* Clone an existing expr. */ Expr * expr_clone( Symbol *sym ) { Expr *expr; if( sym->expr && sym->expr->compile ) { /* Make a new expr, share the compile. */ expr = expr_new( sym ); compile_expr_link_make( sym->expr->compile, expr ); } else { /* No existing expr to copy, make a bare one for the * row, at the same scope level as sym. */ expr = expr_new( sym ); } return( expr ); } /* Mark an expression as containing an error, save the error buffers. */ void * expr_error_set( Expr *expr ) { /* Was not in error? Add to error set. */ if( !expr->err ) { #ifdef DEBUG_ERROR printf( "expr_error_set: error in " ); symbol_name_print( expr->sym ); printf( ": %s %s\n", error_get_top(), error_get_sub() ); #endif /*DEBUG_ERROR*/ IM_SETSTR( expr->error_top, error_get_top() ); IM_SETSTR( expr->error_sub, error_get_sub() ); /* Zap the value of the expr ... it may contain pointers to * dead stuff. */ PEPUTP( &expr->root, ELEMENT_NOVAL, (void *) 99 ); expr_error_all = g_slist_prepend( expr_error_all, expr ); expr->err = TRUE; if( expr->row ) row_error_set( expr->row ); /* If this is the value of a top-level sym, note state * change on symbol. */ if( is_top( expr->sym ) && expr->sym->expr == expr ) symbol_state_change( expr->sym ); } return( NULL ); } /* Extract the error from an expression. */ void expr_error_get( Expr *expr ) { if( !expr->err ) error_clear(); else { g_assert( expr->error_top ); g_assert( expr->error_sub ); error_top( "%s", expr->error_top ); error_sub( "%s", expr->error_sub ); } } /* Clear error state. */ void expr_error_clear( Expr *expr ) { if( expr->err ) { #ifdef DEBUG_ERROR printf( "expr_error_clear: " ); symbol_name_print( expr->sym ); printf( "\n" ); #endif /*DEBUG_ERROR*/ expr->err = FALSE; expr_error_all = g_slist_remove( expr_error_all, expr ); if( expr->row ) row_error_clear( expr->row ); if( is_top( expr->sym ) && expr->sym->expr == expr ) symbol_state_change( expr->sym ); } } /* Mark an expr dirty. * * Two cases: if expr has a row, this is part of a display. Use the row * stuff to mark this expr dirty. Then use symbol_dirty() to mark on from the * root of this row. * * Case two: this must be an expr inside a top-level ... just * symbol_dirty() on from that top level. * * FIXME ... we should be able to scrap this expr_get_root() ... we want the * 'parent' field in the Link we are probably being called from. */ void * expr_dirty( Expr *expr, int serial ) { Row *row; #ifdef DEBUG printf( "expr_dirty: " ); symbol_name_print( expr->sym ); printf( "\n" ); #endif /*DEBUG*/ if( (row = expr_get_row( expr )) && row->top_row->sym ) { Symbol *top_sym = row->top_row->sym; row_dirty( row, TRUE ); symbol_dirty( top_sym, serial ); } else symbol_dirty( expr_get_root( expr )->sym, serial ); return( NULL ); } void * expr_dirty_intrans( Expr *expr, int serial ) { if( expr->row && expr->row->top_row->sym ) { row_dirty_intrans( expr->row, TRUE ); symbol_dirty( expr->row->top_row->sym, serial ); } else symbol_dirty_intrans( expr->sym, serial ); return( NULL ); } void expr_tip_sub( Expr *expr, VipsBuf *buf ) { Compile *compile = expr->compile; if( is_top( expr->sym ) ) { vips_buf_appends( buf, _( "top level" ) ); vips_buf_appends( buf, " " ); } if( compile && is_class( compile ) ) { vips_buf_appends( buf, _( "class" ) ); vips_buf_appends( buf, " " ); if( compile->nparam == 0 ) { vips_buf_appends( buf, _( "instance" ) ); vips_buf_appends( buf, " " ); } else { vips_buf_appends( buf, _( "definition" ) ); vips_buf_appends( buf, " " ); } vips_buf_appendf( buf, "\"%s\"", IOBJECT( expr->sym )->name ); } else if( expr->sym->type == SYM_PARAM ) vips_buf_appendf( buf, _( "parameter \"%s\"" ), IOBJECT( expr->sym )->name ); else if( compile ) { if( is_member( expr->sym ) ) { vips_buf_appends( buf, _( "member" ) ); vips_buf_appends( buf, " " ); } if( compile->nparam == 0 ) { vips_buf_appends( buf, _( "value" ) ); vips_buf_appends( buf, " " ); } else { vips_buf_appends( buf, _( "function" ) ); vips_buf_appends( buf, " " ); } vips_buf_appendf( buf, "\"%s\"", IOBJECT( expr->sym )->name ); } if( !is_top( expr->sym ) ) { vips_buf_appends( buf, " " ); vips_buf_appends( buf, _( "of" ) ); vips_buf_appends( buf, " " ); expr_tip_sub( expr_get_parent( expr ), buf ); } } /* Look at an expr, make a tooltip. */ void expr_tip( Expr *expr, VipsBuf *buf ) { expr_name( expr, buf ); vips_buf_appends( buf, ": " ); expr_tip_sub( expr, buf ); } /* Bind unresolved refs in an expr. Bind for every enclosing dynamic scope. */ void expr_resolve( Expr *expr ) { Expr *top = symbol_root->expr; Expr *i; #ifdef DEBUG printf( "expr_resolve: " ); expr_name_print( expr ); printf( "\n" ); #endif /*DEBUG*/ for( i = expr; i != top; i = expr_get_parent_dynamic( i ) ) /* May try to resolve out through a parameter. */ if( i->compile ) compile_resolve_dynamic( expr->compile, i->compile ); } ================================================ FILE: src/expr.h ================================================ /* Expressions. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_EXPR (expr_get_type()) #define EXPR( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_EXPR, Expr )) #define EXPR_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_EXPR, ExprClass)) #define IS_EXPR( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_EXPR )) #define IS_EXPR_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_EXPR )) #define EXPR_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_EXPR, ExprClass )) /* What we track to parse and compile some text. Can have several * of these for a symbol, all with different values. */ struct _Expr { /* We don't contain anything, but we are contained by Symbol, so we * need to be an iContainer subclass. */ iContainer parent_object; Symbol *sym; /* We are an expr for this symbol, scopewise */ Row *row; /* (optional) we have this display */ Compile *compile; /* Our compiled code */ GSList *static_links; /* Static LinkExprs which reference us */ GSList *dynamic_links; /* Dynamic LinkExprs which reference us */ PElement root; /* Pointer to value of this expr */ /* Are we recorded as having an Imageinfo as a value? Use this to * unlink us from the last ii we were linked to. */ Imageinfo *imageinfo; gboolean err; /* TRUE if there is an error in this expr */ char *error_top; char *error_sub; }; typedef struct _ExprClass { iContainerClass parent_class; /* new_value expr has been recalced and root points to a new piece of graph */ void (*new_value)( Expr *expr ); } ExprClass; extern GSList *expr_error_all; void *expr_error_print( Expr *expr, VipsBuf *buf ); typedef void *(*map_expr_fn)( Expr *, void *, void * ); Expr *expr_map_all( Expr *expr, map_expr_fn fn, void *a ); void *expr_name_print( Expr *expr ); void expr_name( Expr *expr, VipsBuf *buf ); Expr *expr_get_parent( Expr *expr ); Expr *expr_get_root( Expr *expr ); Expr *expr_get_root_dynamic( Expr *expr ); GType expr_get_type( void ); void *expr_strip( Expr *expr ); Expr *expr_new( Symbol *sym ); Expr *expr_clone( Symbol *sym ); /* Set and clear error state. */ void *expr_error_set( Expr *expr ); void expr_error_clear( Expr *expr ); void expr_error_get( Expr *expr ); void expr_link_make( Expr *expr, Symbol *child ); void *expr_link_break( Expr *expr, Symbol *child ); void *expr_dirty( Expr *expr, int serial ); void *expr_dirty_intrans( Expr *expr, int serial ); void expr_tip( Expr *expr, VipsBuf *buf ); void expr_new_value( Expr *expr ); void expr_resolve( Expr *expr ); ================================================ FILE: src/expression.c ================================================ /* an editable expression */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; /* Sub fn. of below. */ static void * expression_get_itext_sub( Row *row ) { Model *itext; /* FIXME ... yuk, map + strcmp could make subcolumn indexed by symbol name? probably not worth it */ if( row->sym && strcmp( IOBJECT( row->sym )->name, MEMBER_EXPR ) == 0 && row->child_rhs && (itext = row->child_rhs->itext) ) return( itext ); return( NULL ); } /* Look down our RHS and try to grab the itext for our MEMBER_EXPR. * Expressionview presents this as the editable formula. * * We can't call the editable member "value", since this imples (elsewhere in * nip anway) an unboxed value. Our editable member could also be boxed .. so * have a different name of reduce confusion a little. Also means we can * define an Expression which inherits from expr. */ iText * expression_get_itext( Expression *expression ) { Row *row = HEAPMODEL( expression )->row; if( row->child_rhs && row->child_rhs->scol ) return( (iText *) subcolumn_map( SUBCOLUMN( row->child_rhs->scol ), (row_map_fn) expression_get_itext_sub, NULL, NULL ) ); return( NULL ); } static View * expression_view_new( Model *model, View *parent ) { return( expressionview_new() ); } static xmlNode * expression_save( Model *model, xmlNode *xnode ) { xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( !set_sprop( xthis, "caption", IOBJECT( model )->caption ) ) return( NULL ); return( xthis ); } static gboolean expression_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { char caption[MAX_STRSIZE]; g_assert( IS_RHS( parent ) ); if( get_sprop( xnode, "caption", caption, MAX_STRSIZE ) ) iobject_set( IOBJECT( model ), NULL, caption ); return( MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ); } /* Update Expression from heap. */ static gboolean expression_class_get( Classmodel *classmodel, PElement *root ) { char caption[MAX_STRSIZE]; #ifdef DEBUG printf( "expression_class_get: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ if( !class_get_member_string( root, MEMBER_CAPTION, caption, MAX_STRSIZE ) ) return( FALSE ); iobject_set( IOBJECT( classmodel ), NULL, caption ); return( TRUE ); } static void expression_class_init( ExpressionClass *class ) { ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ model_class->view_new = expression_view_new; model_class->save = expression_save; model_class->load = expression_load; classmodel_class->class_get = expression_class_get; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void expression_init( Expression *expression ) { iobject_set( IOBJECT( expression ), CLASS_EXPRESSION, NULL ); } GType expression_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ExpressionClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) expression_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Expression ), 32, /* n_pexpressionlocs */ (GInstanceInitFunc) expression_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Expression", &info, 0 ); } return( type ); } ================================================ FILE: src/expression.h ================================================ /* an editable expression in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_EXPRESSION (expression_get_type()) #define EXPRESSION( obj ) (GTK_CHECK_CAST( (obj), TYPE_EXPRESSION, Expression )) #define EXPRESSION_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_EXPRESSION, ExpressionClass )) #define IS_EXPRESSION( obj ) (GTK_CHECK_TYPE( (obj), TYPE_EXPRESSION )) #define IS_EXPRESSION_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_EXPRESSION )) struct _Expression { Classmodel parent_class; /* We don't have a model for the expression: instead we just grab the * value/formula from our MEMBER_VALUE itext. Much simpler. */ }; typedef struct _ExpressionClass { ClassmodelClass parent_class; /* My methods. */ } ExpressionClass; GType expression_get_type( void ); iText *expression_get_itext( Expression *expression ); ================================================ FILE: src/expressionview.c ================================================ /* a view of a text thingy */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; /* Re-read the text in a tally entry. */ static void * expressionview_scan( View *view ) { Expressionview *expressionview = EXPRESSIONVIEW( view ); Expression *expression = EXPRESSION( VOBJECT( expressionview )->iobject ); iText *itext = expression_get_itext( expression ); #ifdef DEBUG { Row *row = HEAPMODEL( expression )->row; printf( "expressionview_scan: " ); row_name_print( row ); printf( "\n" ); } #endif /*DEBUG*/ if( itext && formula_scan( expressionview->formula ) && itext_set_formula( itext, expressionview->formula->expr ) ) { itext_set_edited( itext, TRUE ); /* ... make sure MEMBER_VALUE gets marked dirty too. */ expr_dirty( HEAPMODEL( itext )->row->expr, link_serial_new() ); } return( VIEW_CLASS( parent_class )->scan( view ) ); } void expressionview_activate_cb( GtkWidget *wid, Expressionview *expressionview ) { Expression *expression = EXPRESSION( VOBJECT( expressionview )->iobject ); Row *row = HEAPMODEL( expression )->row; /* Reset edits on this row and all children. */ (void) icontainer_map_all( ICONTAINER( row ), (icontainer_map_fn) heapmodel_clear_edited, NULL ); /* Make sure we scan this text, even if it's not been edited. */ view_scannable_register( VIEW( expressionview ) ); workspace_set_modified( row->ws, TRUE ); symbol_recalculate_all(); } static void expressionview_refresh( vObject *vobject ) { Expressionview *expressionview = EXPRESSIONVIEW( vobject ); Expression *expression = EXPRESSION( VOBJECT( expressionview )->iobject ); iText *itext = expression_get_itext( expression ); Row *row = HEAPMODEL( expression )->row; #ifdef DEBUG printf( "expressionview_refresh: " ); row_name_print( row ); printf( " (%p)\n", vobject ); #endif /*DEBUG*/ formula_set_edit( expressionview->formula, row->ws->mode == WORKSPACE_MODE_FORMULA ); if( itext ) formula_set_value_expr( expressionview->formula, vips_buf_all( &itext->value ), itext->formula ); if( vobject->iobject->caption ) formula_set_caption( expressionview->formula, vobject->iobject->caption ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void expressionview_set_edit( Expressionview *expressionview, gboolean edit ) { formula_set_edit( expressionview->formula, edit ); if( edit ) view_resettable_register( VIEW( expressionview ) ); } static void expressionview_link( View *view, Model *model, View *parent ) { Expressionview *expressionview = EXPRESSIONVIEW( view ); Expression *expression = EXPRESSION( model ); Row *row = HEAPMODEL( expression )->row; #ifdef DEBUG printf( "expressionview_link: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ VIEW_CLASS( parent_class )->link( view, model, parent ); if( GRAPHICVIEW( view )->sview ) gtk_size_group_add_widget( GRAPHICVIEW( view )->sview->group, expressionview->formula->left_label ); /* Edit mode defaults to edit mode for workspace. */ expressionview_set_edit( expressionview, row->ws->mode == WORKSPACE_MODE_FORMULA ); } /* Reset edit mode ... go back to whatever is set for this ws. */ static void expressionview_reset( View *view ) { Expressionview *expressionview = EXPRESSIONVIEW( view ); Expression *expression = EXPRESSION( VOBJECT( expressionview )->iobject ); Row *row = HEAPMODEL( expression )->row; expressionview_set_edit( expressionview, row->ws->mode == WORKSPACE_MODE_FORMULA ); } static void expressionview_class_init( ExpressionviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = expressionview_refresh; view_class->link = expressionview_link; view_class->reset = expressionview_reset; view_class->scan = expressionview_scan; } static void expressionview_init( Expressionview *expressionview ) { expressionview->formula = formula_new(); gtk_signal_connect_object( GTK_OBJECT( expressionview->formula ), "changed", GTK_SIGNAL_FUNC( view_changed_cb ), GTK_OBJECT( expressionview ) ); gtk_signal_connect( GTK_OBJECT( expressionview->formula ), "activate", GTK_SIGNAL_FUNC( expressionview_activate_cb ), expressionview ); gtk_box_pack_start( GTK_BOX( expressionview ), GTK_WIDGET( expressionview->formula ), TRUE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( expressionview->formula ) ); } GtkType expressionview_get_type( void ) { static GtkType expressionview_type = 0; if( !expressionview_type ) { static const GtkTypeInfo expressionview_info = { "Expressionview", sizeof( Expressionview ), sizeof( ExpressionviewClass ), (GtkClassInitFunc) expressionview_class_init, (GtkObjectInitFunc) expressionview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; expressionview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &expressionview_info ); } return( expressionview_type ); } View * expressionview_new( void ) { Expressionview *expressionview = gtk_type_new( TYPE_EXPRESSIONVIEW ); return( VIEW( expressionview ) ); } ================================================ FILE: src/expressionview.h ================================================ /* a textview button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_EXPRESSIONVIEW (expressionview_get_type()) #define EXPRESSIONVIEW( obj ) (GTK_CHECK_CAST( (obj), \ TYPE_EXPRESSIONVIEW, Expressionview )) #define EXPRESSIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_EXPRESSIONVIEW, ExpressionviewClass )) #define IS_EXPRESSIONVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_EXPRESSIONVIEW )) #define IS_EXPRESSIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_EXPRESSIONVIEW )) typedef struct _Expressionview { Graphicview parent_object; Formula *formula; } Expressionview; typedef struct _ExpressionviewClass { GraphicviewClass parent_class; /* My methods. */ } ExpressionviewClass; GtkType expressionview_get_type( void ); View *expressionview_new( void ); ================================================ FILE: src/filemodel.c ================================================ /* abstract base class for things which form the filemodel half of a * filemodel/view pair */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Don't compress save files. FIXME ... some prebuilt libxml2s on win32 don't support libz compression, so don't turn this off */ #define DEBUG_SAVEFILE #include "ip.h" static ModelClass *parent_class = NULL; static GSList *filemodel_registered = NULL; /* Register a file model. Registered models are part of the "xxx has been * modified, save before quit?" check. */ void filemodel_register( Filemodel *filemodel ) { if( !filemodel->registered ) { filemodel->registered = TRUE; filemodel_registered = g_slist_prepend( filemodel_registered, filemodel ); #ifdef DEBUG printf( "filemodel_register: %s \"%s\" (%p)\n", G_OBJECT_TYPE_NAME( filemodel ), IOBJECT( filemodel )->name, filemodel ); #endif /*DEBUG*/ } } void filemodel_unregister( Filemodel *filemodel ) { if( filemodel->registered ) { filemodel->registered = FALSE; filemodel_registered = g_slist_remove( filemodel_registered, filemodel ); #ifdef DEBUG printf( "filemodel_unregister: %s \"%s\" (%p)\n", G_OBJECT_TYPE_NAME( filemodel ), IOBJECT( filemodel )->name, filemodel ); #endif /*DEBUG*/ } } /* Trigger the top_load method for a filemodel. */ void * filemodel_top_load( Filemodel *filemodel, ModelLoadState *state, Model *parent, xmlNode *xnode ) { FilemodelClass *filemodel_class = FILEMODEL_GET_CLASS( filemodel ); if( filemodel_class->top_load ) { if( !filemodel_class->top_load( filemodel, state, parent, xnode ) ) return( filemodel ); } else { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "top_load", G_OBJECT_CLASS_NAME( filemodel_class ) ); return( filemodel ); } return( NULL ); } /* Trigger the set_modified method for a filemodel. */ void filemodel_set_modified( Filemodel *filemodel, gboolean modified ) { FilemodelClass *filemodel_class = FILEMODEL_GET_CLASS( filemodel ); if( filemodel_class->set_modified ) filemodel_class->set_modified( filemodel, modified ); } void filemodel_set_window_hint( Filemodel *filemodel, iWindow *iwnd ) { /* This can be called repeatedly if objects are moved between windows. */ filemodel->window_hint = iwnd; } iWindow * filemodel_get_window_hint( Filemodel *filemodel ) { if( filemodel->window_hint ) return( filemodel->window_hint ); else return( IWINDOW( mainw_pick_one() ) ); } gboolean filemodel_top_save( Filemodel *filemodel, const char *filename ) { FilemodelClass *filemodel_class = FILEMODEL_GET_CLASS( filemodel ); if( filemodel_class->top_save ) { char *old_filename; int result; /* We must always have the new filename in the save file or * auto path rewriting will get confused on reload. * * Equally, we must not change the filename on the model, in * case this save is not something initiated by the user, for * example, an auto-backup of the workspace. * * Save and restore the filename. Our caller must set the * final filename, if required (after save-as, for example). */ old_filename = g_strdup( filemodel->filename ); filemodel_set_filename( filemodel, filename ); result = filemodel_class->top_save( filemodel, filename ); filemodel_set_filename( filemodel, old_filename ); g_free( old_filename ); return( result ); } else { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "top_save", G_OBJECT_CLASS_NAME( filemodel_class ) ); return( FALSE ); } } static void filemodel_info( iObject *iobject, VipsBuf *buf ) { Filemodel *filemodel = FILEMODEL( iobject ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); vips_buf_appendf( buf, "filename = \"%s\"\n", NN( filemodel->filename ) ); vips_buf_appendf( buf, "modified = \"%s\"\n", bool_to_char( filemodel->modified ) ); vips_buf_appendf( buf, "registered = \"%s\"\n", bool_to_char( filemodel->registered ) ); vips_buf_appendf( buf, "auto_load = \"%s\"\n", bool_to_char( filemodel->auto_load ) ); } /* filename can be NULL for unset. */ void filemodel_set_filename( Filemodel *filemodel, const char *filename ) { if( filemodel->filename != filename ) { char buf[FILENAME_MAX]; /* We want to keep the absolute, compact form of the filename * inside the object so we don't get a dependency on CWD. */ if( filename ) { im_strncpy( buf, filename, FILENAME_MAX ); path_compact( buf ); filename = buf; } IM_SETSTR( filemodel->filename, filename ); iobject_changed( IOBJECT( filemodel ) ); } } static void filemodel_finalize( GObject *gobject ) { Filemodel *filemodel; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_FILEMODEL( gobject ) ); filemodel = FILEMODEL( gobject ); #ifdef DEBUG printf( "filemodel_finalize: %s \"%s\" (%s)\n", G_OBJECT_TYPE_NAME( filemodel ), NN( IOBJECT( filemodel )->name ), NN( filemodel->filename ) ); #endif /*DEBUG*/ IM_FREE( filemodel->filename ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void filemodel_dispose( GObject *gobject ) { Filemodel *filemodel; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_FILEMODEL( gobject ) ); filemodel = FILEMODEL( gobject ); #ifdef DEBUG printf( "filemodel_dispose: %s \"%s\" (%s)\n", G_OBJECT_TYPE_NAME( filemodel ), NN( IOBJECT( filemodel )->name ), NN( filemodel->filename ) ); #endif /*DEBUG*/ filemodel_unregister( filemodel ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static xmlNode * filemodel_save( Model *model, xmlNode *xnode ) { Filemodel *filemodel = FILEMODEL( model ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( !set_sprop( xthis, "filename", filemodel->filename ) ) return( NULL ); return( xthis ); } static gboolean filemodel_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { Filemodel *filemodel = FILEMODEL( model ); char buf[MAX_STRSIZE]; if( get_sprop( xnode, "filename", buf, MAX_STRSIZE ) ) filemodel_set_filename( filemodel, buf ); if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ) return( FALSE ); return( TRUE ); } static gboolean filemodel_real_top_load( Filemodel *filemodel, ModelLoadState *state, Model *parent, xmlNode *xnode ) { return( TRUE ); } static void filemodel_real_set_modified( Filemodel *filemodel, gboolean modified ) { if( filemodel->modified != modified ) { #ifdef DEBUG printf( "filemodel_real_set_modified: %s \"%s\" (%s) %s\n", G_OBJECT_TYPE_NAME( filemodel ), NN( IOBJECT( filemodel )->name ), NN( filemodel->filename ), bool_to_char( modified ) ); #endif /*DEBUG*/ filemodel->modified = modified; iobject_changed( IOBJECT( filemodel ) ); } } static int filemodel_xml_save_format_file( const char *filename, xmlDoc *doc ) { return( xmlSaveFormatFile( filename, doc, 1 ) == -1 ); } /* Save to filemodel->filename. */ static gboolean filemodel_top_save_xml( Filemodel *filemodel, const char *filename ) { xmlDoc *xdoc; char namespace[256]; if( !(xdoc = xmlNewDoc( (xmlChar *) "1.0" )) ) { error_top( _( "XML library error." ) ); error_sub( _( "model_save_filename: xmlNewDoc() failed" ) ); return( FALSE ); } #ifndef DEBUG_SAVEFILE xmlSetDocCompressMode( xdoc, 1 ); #endif /*!DEBUG_SAVEFILE*/ im_snprintf( namespace, 256, "%s/%d.%d.%d", NAMESPACE, filemodel->major, filemodel->minor, filemodel->micro ); if( !(xdoc->children = xmlNewDocNode( xdoc, NULL, (xmlChar *) "root", NULL )) || !set_sprop( xdoc->children, "xmlns", namespace ) ) { error_top( _( "XML library error." ) ); error_sub( _( "model_save_filename: xmlNewDocNode() failed" ) ); xmlFreeDoc( xdoc ); return( FALSE ); } column_set_offset( filemodel->x_off, filemodel->y_off ); if( model_save( MODEL( filemodel ), xdoc->children ) ) { xmlFreeDoc( xdoc ); return( FALSE ); } if( calli_string_filename( (calli_string_fn) filemodel_xml_save_format_file, filename, xdoc, NULL, NULL ) ) { error_top( _( "Save failed." ) ); error_sub( _( "Save of %s \"%s\" to file \"%s\" failed.\n%s" ), IOBJECT_GET_CLASS_NAME( filemodel ), NN( IOBJECT( filemodel )->name ), NN( filename ), g_strerror( errno ) ); xmlFreeDoc( xdoc ); return( FALSE ); } xmlFreeDoc( xdoc ); return( TRUE ); } static gboolean filemodel_top_save_text( Filemodel *filemodel, const char *filename ) { iOpenFile *of; if( !(of = ifile_open_write( "%s", filename )) ) return( FALSE ); column_set_offset( filemodel->x_off, filemodel->y_off ); if( model_save_text( MODEL( filemodel ), of ) ) { ifile_close( of ); return( FALSE ); } ifile_close( of ); return( TRUE ); } static gboolean filemodel_real_top_save( Filemodel *filemodel, const char *filename ) { ModelClass *model_class = MODEL_GET_CLASS( filemodel ); #ifdef DEBUG printf( "filemodel_real_top_save: save %s \"%s\" to file \"%s\"\n", G_OBJECT_TYPE_NAME( filemodel ), NN( IOBJECT( filemodel )->name ), filename ); #endif /*DEBUG*/ if( model_class->save_text ) { if( !filemodel_top_save_text( filemodel, filename ) ) return( FALSE ); } else if( model_class->save ) { if( !filemodel_top_save_xml( filemodel, filename ) ) return( FALSE ); } else { error_top( _( "Not implemented." ) ); error_sub( _( "filemodel_real_top_save: no save method" ) ); return( FALSE ); } return( TRUE ); } static void filemodel_class_init( FilemodelClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); ModelClass *model_class = (ModelClass*) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = filemodel_finalize; gobject_class->dispose = filemodel_dispose; iobject_class->info = filemodel_info; model_class->save = filemodel_save; model_class->load = filemodel_load; class->top_load = filemodel_real_top_load; class->set_modified = filemodel_real_set_modified; class->top_save = filemodel_real_top_save; /* NULL isn't an allowed value -- this gets overridden by our * subclasses. */ class->filetype = NULL; class->filetype_pref = NULL; } static void filemodel_init( Filemodel *filemodel ) { /* Init our instance fields. */ filemodel->filename = NULL; filemodel->modified = FALSE; filemodel->registered = FALSE; filemodel->auto_load = FALSE; filemodel->x_off = 0; filemodel->y_off = 0; /* Default version. */ filemodel->versioned = FALSE; filemodel->major = MAJOR_VERSION; filemodel->minor = MINOR_VERSION; filemodel->micro = MICRO_VERSION; filemodel->window_hint = NULL; } GtkType filemodel_get_type( void ) { static GtkType filemodel_type = 0; if( !filemodel_type ) { static const GTypeInfo info = { sizeof( FilemodelClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) filemodel_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Filemodel ), 32, /* n_preallocs */ (GInstanceInitFunc) filemodel_init, }; filemodel_type = g_type_register_static( TYPE_MODEL, "Filemodel", &info, 0 ); } return( filemodel_type ); } void filemodel_set_offset( Filemodel *filemodel, int x_off, int y_off ) { #ifdef DEBUG printf( "filemodel_set_offset: %s \"%s\" %d x %d\n", G_OBJECT_TYPE_NAME( filemodel ), NN( IOBJECT( filemodel )->name ), x_off, y_off ); #endif /*DEBUG*/ filemodel->x_off = x_off; filemodel->y_off = y_off; } static gboolean filemodel_load_all_xml( Filemodel *filemodel, Model *parent, ModelLoadState *state ) { xmlNode *xnode; /* Check the root element for type/version compatibility. */ if( !(xnode = xmlDocGetRootElement( state->xdoc )) || !xnode->nsDef || !is_prefix( NAMESPACE, (char *) xnode->nsDef->href ) ) { error_top( _( "Load failed." ) ); error_sub( _( "Can't load XML file \"%s\", " "it's not a %s save file." ), state->filename, PACKAGE ); return( FALSE ); } if( sscanf( (char *) xnode->nsDef->href + strlen( NAMESPACE ) + 1, "%d.%d.%d", &state->major, &state->minor, &state->micro ) != 3 ) { error_top( _( "Load failed." ) ); error_sub( _( "Can't load XML file \"%s\", " "unable to extract version information from " "namespace." ), state->filename ); return( FALSE ); } #ifdef DEBUG printf( "filemodel_load_all_xml: major = %d, minor = %d, micro = %d\n", state->major, state->minor, state->micro ); #endif /*DEBUG*/ if( filemodel_top_load( filemodel, state, parent, xnode ) ) return( FALSE ); return( TRUE ); } static gboolean filemodel_load_all_xml_file( Filemodel *filemodel, Model *parent, const char *filename, const char *filename_user ) { ModelLoadState *state; if( !(state = model_loadstate_new( filename, filename_user )) ) return( FALSE ); if( !filemodel_load_all_xml( filemodel, parent, state ) ) { model_loadstate_destroy( state ); return( FALSE ); } model_loadstate_destroy( state ); return( TRUE ); } static gboolean filemodel_load_all_xml_openfile( Filemodel *filemodel, Model *parent, iOpenFile *of ) { ModelLoadState *state; if( !(state = model_loadstate_new_openfile( of )) ) return( FALSE ); if( !filemodel_load_all_xml( filemodel, parent, state ) ) { model_loadstate_destroy( state ); return( FALSE ); } model_loadstate_destroy( state ); return( TRUE ); } static gboolean filemodel_load_all_text( Filemodel *filemodel, Model *parent, const char *filename, const char *filename_user ) { iOpenFile *of; if( !(of = ifile_open_read( "%s", filename )) ) return( FALSE ); if( model_load_text( MODEL( filemodel ), parent, of ) ) { ifile_close( of ); return( FALSE ); } ifile_close( of ); return( TRUE ); } /* Load filename into filemodel ... can mean merge as well as init. * * We load from @filename. If @filename_user is non-NULL, that's the filename * we should record in the model. */ gboolean filemodel_load_all( Filemodel *filemodel, Model *parent, const char *filename, const char *filename_user ) { ModelClass *model_class = MODEL_GET_CLASS( filemodel ); const char *tname = G_OBJECT_CLASS_NAME( model_class ); #ifdef DEBUG printf( "filemodel_load_all: load file \"%s\" into parent %s \"%s\"\n", filename, G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) ); #endif /*DEBUG*/ if( model_class->load_text ) { if( !filemodel_load_all_text( filemodel, parent, filename, filename_user ) ) return( FALSE ); } else if( model_class->load ) { if( !filemodel_load_all_xml_file( filemodel, parent, filename, filename_user ) ) return( FALSE ); } else { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "load", tname ); return( FALSE ); } /* Don't recomp here, we may be loading a bunch of interdependent * files. */ return( TRUE ); } /* Load iOpenFile into filemodel ... can mean merge as well as init. */ gboolean filemodel_load_all_openfile( Filemodel *filemodel, Model *parent, iOpenFile *of ) { ModelClass *model_class = MODEL_GET_CLASS( filemodel ); const char *tname = G_OBJECT_CLASS_NAME( model_class ); #ifdef DEBUG printf( "filemodel_load_all_openfile: load \"%s\" " "into parent %s \"%s\"\n", of->fname, G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) ); #endif /*DEBUG*/ if( model_class->load_text ) { if( model_load_text( MODEL( filemodel ), parent, of ) ) return( FALSE ); } else if( model_class->load ) { if( !filemodel_load_all_xml_openfile( filemodel, parent, of ) ) return( FALSE ); } else { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "load", tname ); return( FALSE ); } /* Don't recomp here, we may be loading a bunch of interdependent * files. */ return( TRUE ); } /* Interactive stuff ... save first. */ static void filemodel_inter_saveas_sub_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Filemodel *filemodel = FILEMODEL( client ); char *filename; if( (filename = filesel_get_filename( filesel )) ) { if( filemodel_top_save( filemodel, filename ) ) { filemodel_set_filename( filemodel, filename ); filemodel_set_modified( filemodel, FALSE ); nfn( sys, IWINDOW_YES ); } else nfn( sys, IWINDOW_ERROR ); g_free( filename ); } else nfn( sys, IWINDOW_ERROR ); } static void filemodel_inter_saveas_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filemodel *filemodel = FILEMODEL( client ); FilemodelClass *class = FILEMODEL_GET_CLASS( filemodel ); Filesel *filesel = FILESEL( filesel_new() ); /* Expands to (eg.) "Save Column A2". */ iwindow_set_title( IWINDOW( filesel ), _( "Save %s %s" ), IOBJECT_GET_CLASS_NAME( filemodel ), NN( IOBJECT( filemodel )->name ) ); filesel_set_flags( filesel, FALSE, TRUE ); filesel_set_filetype( filesel, class->filetype, watch_int_get( main_watchgroup, class->filetype_pref, 0 ) ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) ); filesel_set_done( filesel, filemodel_inter_saveas_sub_cb, filemodel ); idialog_set_notify( IDIALOG( filesel ), nfn, sys ); iwindow_build( IWINDOW( filesel ) ); if( filemodel->filename ) filesel_set_filename( filesel, filemodel->filename ); gtk_widget_show( GTK_WIDGET( filesel ) ); } void filemodel_inter_saveas( iWindow *parent, Filemodel *filemodel ) { filemodel_inter_saveas_cb( parent, filemodel, iwindow_notify_null, NULL ); } void filemodel_inter_save( iWindow *parent, Filemodel *filemodel ) { if( filemodel->filename ) { if( !filemodel_top_save( filemodel, filemodel->filename ) ) iwindow_alert( GTK_WIDGET( parent ), GTK_MESSAGE_ERROR ); else filemodel_set_modified( filemodel, FALSE ); } else filemodel_inter_saveas( parent, filemodel ); } /* Now "empty" ... do an 'are you sure' check if modified has been set. */ static void filemodel_inter_empty_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filemodel *filemodel = FILEMODEL( client ); (void) model_empty( MODEL( filemodel ) ); filemodel_set_modified( filemodel, FALSE ); nfn( sys, IWINDOW_YES ); } static void filemodel_inter_savenempty_ok_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { iWindowSusp *susp = iwindow_susp_new( filemodel_inter_empty_cb, iwnd, client, nfn, sys ); filemodel_inter_saveas_cb( iwnd, client, iwindow_susp_comp, susp ); } void filemodel_inter_savenempty_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filemodel *filemodel = FILEMODEL( client ); const char *tname = IOBJECT_GET_CLASS_NAME( filemodel ); if( filemodel->modified ) { if( filemodel->filename ) box_savenosave( GTK_WIDGET( iwnd ), filemodel_inter_savenempty_ok_cb, filemodel_inter_empty_cb, filemodel, nfn, sys, _( "Object has been modified." ), _( "%s has been modified since you " "loaded it from file \"%s\".\n\n" "Do you want to save your changes?" ), tname, NN( filemodel->filename ) ); else box_savenosave( GTK_WIDGET( iwnd ), filemodel_inter_savenempty_ok_cb, filemodel_inter_empty_cb, filemodel, nfn, sys, _( "Object has been modified." ), _( "%s has been modified. " "Do you want to save your changes?" ), tname ); } else filemodel_inter_empty_cb( NULL, filemodel, nfn, sys ); } void filemodel_inter_savenempty( iWindow *parent, Filemodel *filemodel ) { filemodel_inter_savenempty_cb( parent, filemodel, iwindow_notify_null, NULL ); } /* Now "close" ... easy: just savenempty, then destroy. */ static void filemodel_inter_close_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filemodel *filemodel = FILEMODEL( client ); iwindow_kill( filemodel_get_window_hint( filemodel ) ); nfn( sys, IWINDOW_YES ); } void filemodel_inter_savenclose_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { iWindowSusp *susp = iwindow_susp_new( filemodel_inter_close_cb, iwnd, client, nfn, sys ); filemodel_inter_savenempty_cb( iwnd, client, iwindow_susp_comp, susp ); } void filemodel_inter_savenclose( iWindow *parent, Filemodel *filemodel ) { filemodel_inter_savenclose_cb( parent, filemodel, iwindow_notify_null, NULL ); } /* Now "load" ... add stuff to a model from a file. */ static void filemodel_inter_load_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Filemodel *filemodel = FILEMODEL( client ); iContainer *parent = ICONTAINER( filemodel )->parent; char *filename; if( (filename = filesel_get_filename( filesel )) ) { filemodel_set_filename( filemodel, filename ); if( filemodel_load_all( filemodel, MODEL( parent ), filename, NULL ) ) nfn( sys, IWINDOW_YES ); else nfn( sys, IWINDOW_ERROR ); g_free( filename ); } else nfn( sys, IWINDOW_ERROR ); } static void filemodel_inter_loadas_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filemodel *filemodel = FILEMODEL( client ); FilemodelClass *class = FILEMODEL_GET_CLASS( filemodel ); Filesel *filesel = FILESEL( filesel_new() ); iwindow_set_title( IWINDOW( filesel ), "Load %s", IOBJECT_GET_CLASS_NAME( filemodel ) ); filesel_set_flags( filesel, FALSE, TRUE ); filesel_set_filetype( filesel, class->filetype, watch_int_get( main_watchgroup, class->filetype_pref, 0 ) ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) ); filesel_set_done( filesel, filemodel_inter_load_cb, filemodel ); idialog_set_notify( IDIALOG( filesel ), nfn, sys ); iwindow_build( IWINDOW( filesel ) ); if( filemodel->filename ) filesel_set_filename( filesel, filemodel->filename ); gtk_widget_show( GTK_WIDGET( filesel ) ); } void filemodel_inter_loadas( iWindow *parent, Filemodel *filemodel ) { filemodel_inter_loadas_cb( parent, filemodel, iwindow_notify_null, NULL ); } /* Finally "replace" ... empty, then load. */ static void filemodel_inter_replace_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { iWindowSusp *susp = iwindow_susp_new( filemodel_inter_loadas_cb, iwnd, client, nfn, sys ); filemodel_inter_savenempty_cb( iwnd, client, iwindow_susp_comp, susp ); } void filemodel_inter_replace( iWindow *parent, Filemodel *filemodel ) { filemodel_inter_replace_cb( parent, filemodel, iwindow_notify_null, NULL ); } /* Close all registered filemodels. */ /* The first registered, modified filemodel the user hasn't said "ok!!! ffs" * to. */ static Filemodel * filemodel_inter_close_get_filemodel( void ) { GSList *p; for( p = filemodel_registered; p; p = p->next ) { Filemodel *filemodel = FILEMODEL( p->data ); if( filemodel->modified ) return( filemodel ); } return( NULL ); } void filemodel_inter_close_registered_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filemodel *filemodel; if( (filemodel = filemodel_inter_close_get_filemodel()) ) { iWindowSusp *susp = iwindow_susp_new( filemodel_inter_close_registered_cb, iwnd, client, nfn, sys ); filemodel_inter_savenclose_cb( filemodel_get_window_hint( filemodel ), filemodel, iwindow_susp_comp, susp ); } else nfn( sys, IWINDOW_YES ); } /* Mark something as having been loaded (or made) during startup. If we loaded * from one of the system areas, zap the filename so that we will save to the * user's area on changes. */ void filemodel_set_auto_load( Filemodel *filemodel ) { filemodel->auto_load = TRUE; /* FIXME ... not very futureproof */ if( filemodel->filename && strstr( filemodel->filename, "share" G_DIR_SEPARATOR_S PACKAGE ) ) { char *p = strrchr( filemodel->filename, G_DIR_SEPARATOR ); char buf[FILENAME_MAX]; g_assert( p ); im_snprintf( buf, FILENAME_MAX, "$SAVEDIR" G_DIR_SEPARATOR_S "start" G_DIR_SEPARATOR_S "%s", p + 1 ); filemodel_set_filename( filemodel, buf ); } } ================================================ FILE: src/filemodel.h ================================================ /* abstract base class for things which are loaded or saved from files */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define FILEMODEL_LOAD_STATE( obj ) ((FilemodelLoadState *) obj) #define TYPE_FILEMODEL (filemodel_get_type()) #define FILEMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_FILEMODEL, Filemodel )) #define FILEMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_FILEMODEL, FilemodelClass)) #define IS_FILEMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_FILEMODEL )) #define IS_FILEMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_FILEMODEL )) #define FILEMODEL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_FILEMODEL, FilemodelClass )) struct _Filemodel { Model model; char *filename; /* File we read this thing from */ gboolean modified; /* Set if modified (and should be saved) */ gboolean registered; /* Set if on list of things to save on quit */ gboolean auto_load; /* TRUE if loaded from path_start */ int x_off, y_off; /* Save offset for things below this */ /* When we loaded this filemodel, the version numbers we saw in the * XML file. */ gboolean versioned; /* Set means from a versioned file */ int major; int minor; int micro; iWindow *window_hint; /* Our views set this as a hint */ }; typedef struct _FilemodelClass { ModelClass parent_class; /* top_load top level load function ... controls how the rest of the load happens ... eg. merge, rename, etc. set_modified set/clear the modified state top_save top level save ... intercept this to override */ gboolean (*top_load)( Filemodel *filemodel, ModelLoadState *state, Model *parent, xmlNode *xnode ); void (*set_modified)( Filemodel *filemodel, gboolean modified ); gboolean (*top_save)( Filemodel *filemodel, const char *filename ); FileselFileType **filetype; const char *filetype_pref; } FilemodelClass; void filemodel_register( Filemodel *filemodel ); void filemodel_unregister( Filemodel *filemodel ); void *filemodel_top_load( Filemodel *filemodel, ModelLoadState *state, Model *parent, xmlNode *xnode ); void filemodel_set_filename( Filemodel *filemodel, const char *filename ); void filemodel_set_modified( Filemodel *filemodel, gboolean state ); void filemodel_set_window_hint( Filemodel *filemodel, iWindow *iwnd ); iWindow *filemodel_get_window_hint( Filemodel *filemodel ); GType filemodel_get_type( void ); void filemodel_set_offset( Filemodel *filemodel, int x_off, int y_off ); gboolean filemodel_top_save( Filemodel *filemodel, const char *filename ); gboolean filemodel_load_all( Filemodel *filemodel, Model *parent, const char *filename, const char *filename_user ); gboolean filemodel_load_all_openfile( Filemodel *filemodel, Model *parent, iOpenFile *of ); void filemodel_inter_saveas( iWindow *parent, Filemodel *filemodel ); void filemodel_inter_save( iWindow *parent, Filemodel *filemodel ); void filemodel_inter_savenempty_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ); void filemodel_inter_savenempty( iWindow *parent, Filemodel *filemodel ); void filemodel_inter_savenclose_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ); void filemodel_inter_savenclose( iWindow *parent, Filemodel *filemodel ); void filemodel_inter_loadas( iWindow *parent, Filemodel *filemodel ); void filemodel_inter_replace( iWindow *parent, Filemodel *filemodel ); void filemodel_inter_close_registered_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ); void filemodel_set_auto_load( Filemodel *filemodel ); ================================================ FILE: src/filesel.c ================================================ /* ip's file selectors. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* Define for debugging output. #define DEBUG */ /* TIFF save possibilities. Needs to be kept in sync with the Option in * preferences. */ typedef enum { TIFF_COMPRESSION_NONE = 0, /* No compression */ TIFF_COMPRESSION_LZW, /* Lempel-Ziv compression */ TIFF_COMPRESSION_DEFLATE, /* Zip (deflate) compression */ TIFF_COMPRESSION_PACKBITS, /* Packbits compression */ TIFF_COMPRESSION_JPEG, /* JPEG compression */ TIFF_COMPRESSION_CCITTFAX4 /* Fax compression */ } TiffCompression; typedef enum { TIFF_LAYOUT_STRIP = 0, /* Strip TIFF */ TIFF_LAYOUT_TILE /* Tiled TIFF */ } TiffLayout; typedef enum { TIFF_MULTIRES_FLAT = 0, /* Flat file */ TIFF_MULTIRES_PYRAMID /* Pyramidal TIFF */ } TiffMultires; typedef enum { TIFF_FORMAT_MANYBIT = 0, /* No bit reduction */ TIFF_FORMAT_ONEBIT /* Reduce to 1 bit, where poss */ } TiffFormat; /* Keep a list of all filesels currently active ... we use this for refresh on * new file. */ static GSList *filesel_all = NULL; static iDialogClass *parent_class = NULL; /* For filesels which don't have a suggested filename, track the last dir we * went to and use that as the start dir next time. */ static char *filesel_last_dir = NULL; static const char *icc_suffs[] = { ".icc", ".icm", NULL }; static const char *workspace_suffs[] = { ".ws", NULL }; static const char *rec_suffs[] = { ".rec", NULL }; static const char *mor_suffs[] = { ".mor", NULL }; static const char *con_suffs[] = { ".con", NULL }; static const char *mat_suffs[] = { ".mat", NULL }; static const char *def_suffs[] = { ".def", NULL }; static const char *all_suffs[] = { "", NULL }; FileselFileType filesel_wfile_type = { N_( "Workspace files (*.ws)" ), workspace_suffs }, filesel_rfile_type = { N_( "Recombination matrix files (*.rec)" ), rec_suffs }, filesel_mfile_type = { N_( "Morphology matrix files (*.mor)" ), mor_suffs }, filesel_cfile_type = { N_( "Convolution matrix files (*.con)" ), con_suffs }, filesel_xfile_type = { N_( "Matrix files (*.mat)" ), mat_suffs }, filesel_dfile_type = { N_( "Definition files (*.def)" ), def_suffs }, filesel_ifile_type = { N_( "ICC profiles (*.icc, *.icm)" ), icc_suffs }, filesel_allfile_type = { N_( "All files (*)" ), all_suffs }; FileselFileType *filesel_type_definition[] = { &filesel_dfile_type, NULL }, *filesel_type_workspace[] = { &filesel_wfile_type, NULL }, *filesel_type_matrix[] = { &filesel_xfile_type, &filesel_cfile_type, &filesel_rfile_type, &filesel_mfile_type, NULL }, /* Set during startup. */ **filesel_type_image = NULL, **filesel_type_mainw = NULL, **filesel_type_any = NULL; static void * build_vips_formats_sub( VipsFormatClass *format, GSList **types ) { FileselFileType *type = g_new( FileselFileType, 1 ); char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); const char **i; vips_buf_appendf( &buf, "%s ", VIPS_OBJECT_CLASS( format )->description ); /* Used as eg. "VIPS image files (*.v)" */ vips_buf_appends( &buf, _( "image files" ) ); vips_buf_appends( &buf, " (" ); if( *format->suffs ) for( i = format->suffs; *i; i++ ) { vips_buf_appendf( &buf, "*%s", *i ); if( i[1] ) vips_buf_appends( &buf, "; " ); } else /* No suffix means any allowed. */ vips_buf_appendf( &buf, "*" ); vips_buf_appends( &buf, ")" ); type->name = g_strdup( vips_buf_all( &buf ) ); type->suffixes = format->suffs; *types = g_slist_append( *types, type ); return( NULL ); } /* Look at the registered VIPS formats, build a file type list. Call from * filesel class init. */ static FileselFileType ** build_image_file_type( void ) { GSList *types; FileselFileType **type_array; types = NULL; vips_format_map( (VSListMap2Fn) build_vips_formats_sub, &types, NULL ); type_array = (FileselFileType **) slist_to_array( types ); g_slist_free( types ); return( type_array ); } /* Combine a NULL-terminated list of FileselFileType arrays into one. */ static FileselFileType ** build_file_type_va( FileselFileType **first, ... ) { va_list args; int len; FileselFileType **i; FileselFileType **array; int j; int k; /* Count total number of items. */ len = 0; va_start( args, first ); for( i = first; i; i = va_arg( args, FileselFileType ** ) ) len += array_len( (void **) i ); va_end( args ); /* Copy and NULL-terminate. */ array = g_new( FileselFileType *, len + 1 ); va_start( args, first ); j = 0; for( i = first; i; i = va_arg( args, FileselFileType ** ) ) for( k = 0; i[k]; k++ ) array[j++] = i[k]; va_end( args ); array[j] = NULL; return( array ); } /* Here from main() during startup. We can't just put this in class_init, * because we want to be sure this happens early on. */ void filesel_startup( void ) { filesel_type_image = build_image_file_type(); filesel_type_mainw = build_file_type_va( filesel_type_image, filesel_type_matrix, filesel_type_workspace, NULL ); filesel_type_any = build_file_type_va( filesel_type_mainw, filesel_type_definition, NULL ); } /* Is a file of type ... just look at the suffix. */ gboolean is_file_type( const FileselFileType *type, const char *filename ) { const char **p; const char *suf; if( (suf = strrchr( filename, '.' )) ) { for( p = type->suffixes; *p; p++ ) if( strcasecmp( suf, *p ) == 0 ) return( TRUE ); } return( FALSE ); } /* Map TIFF formats to char* for VIPS. */ static char * decode_tiff_compression( TiffCompression tc ) { switch( tc ) { case TIFF_COMPRESSION_LZW: return( "lzw" ); case TIFF_COMPRESSION_DEFLATE: return( "deflate" ); case TIFF_COMPRESSION_PACKBITS: return( "packbits" ); case TIFF_COMPRESSION_JPEG: return( "jpeg" ); case TIFF_COMPRESSION_CCITTFAX4:return( "ccittfax4" ); case TIFF_COMPRESSION_NONE: default: return( "none" ); } } static char * decode_tiff_layout( TiffLayout tf ) { switch( tf ) { case TIFF_LAYOUT_TILE: return( "tile" ); case TIFF_LAYOUT_STRIP: default: return( "strip" ); } } static char * decode_tiff_multires( TiffMultires tm ) { switch( tm ) { case TIFF_MULTIRES_PYRAMID: return( "pyramid" ); case TIFF_MULTIRES_FLAT: default: return( "flat" ); } } static char * decode_tiff_format( TiffFormat tm ) { switch( tm ) { case TIFF_FORMAT_ONEBIT: return( "onebit" ); case TIFF_FORMAT_MANYBIT: default: return( "manybit" ); } } /* Make a TIFF save format string. */ static void filesel_tiff_mode( char *out ) { char ctype[FILENAME_MAX]; char ltype[FILENAME_MAX]; char buf[FILENAME_MAX]; strcpy( ctype, decode_tiff_compression( IP_TIFF_COMPRESSION ) ); if( IP_TIFF_COMPRESSION == TIFF_COMPRESSION_JPEG ) { im_snprintf( buf, FILENAME_MAX, ":%d", IP_TIFF_JPEG_Q ); strcat( ctype, buf ); } if( IP_TIFF_COMPRESSION == TIFF_COMPRESSION_DEFLATE || IP_TIFF_COMPRESSION == TIFF_COMPRESSION_LZW ) { im_snprintf( buf, FILENAME_MAX, ":%d", IP_TIFF_PREDICTOR + 1 ); strcat( ctype, buf ); } strcpy( ltype, decode_tiff_layout( IP_TIFF_LAYOUT ) ); if( IP_TIFF_LAYOUT == TIFF_LAYOUT_TILE ) { im_snprintf( buf, FILENAME_MAX, ":%dx%d", IP_TIFF_TILE_WIDTH, IP_TIFF_TILE_WIDTH ); strcat( ltype, buf ); } im_snprintf( out, 256, "%s,%s,%s,%s,,,%s", ctype, ltype, decode_tiff_multires( IP_TIFF_MULTI_RES ), decode_tiff_format( IP_TIFF_FORMAT ), IP_TIFF_BIGTIFF ? "8" : "" ); } /* Make a JPEG save format string. */ static void filesel_jpeg_mode( char *out ) { char profile[FILENAME_MAX]; switch( IP_JPEG_ICC_PROFILE ) { case 0: /* Use embedded profile ... do nothing. */ strcpy( profile, "" ); break; case 1: { /* Embed from file. */ char buf[FILENAME_MAX]; char buf2[FILENAME_MAX]; im_strncpy( buf, IP_JPEG_ICC_PROFILE_FILE, FILENAME_MAX ); expand_variables( buf, buf2 ); nativeize_path( buf2 ); im_snprintf( profile, FILENAME_MAX, ",%s", buf2 ); break; } case 2: /* Don't attach a profile. */ im_snprintf( profile, FILENAME_MAX, ",none" ); break; default: /* Again, do nothing. */ strcpy( profile, "" ); break; } im_snprintf( out, 256, "%d%s", IP_JPEG_Q, profile ); } /* Make a PNG save format string. */ static void filesel_png_mode( char *out ) { im_snprintf( out, 256, "%d,%d", IP_PNG_COMPRESSION, IP_PNG_INTERLACE ); } /* Make a PPM save format string. */ static void filesel_ppm_mode( char *out ) { switch( IP_PPM_MODE ) { case 0: im_snprintf( out, 256, "binary" ); break; default: im_snprintf( out, 256, "ascii" ); break; } } /* Make a CSV save format string. */ static void filesel_csv_mode( char *out ) { /* We have to escape ":" and "," characters in the separator string. */ char separator[256]; escape_mode( IP_CSV_SEPARATOR, separator, 256 ); im_snprintf( out, 256, "sep:%s", separator ); } typedef void (*make_mode_fn)( char *buf ); typedef struct { const char *caption_filter; /* nip2 column name for the format */ const char *name; /* vips nickname for the format */ make_mode_fn mode_fn; /* Build a mode string */ } FileselMode; static FileselMode filesel_mode_table[] = { { "JPEG", "jpeg", filesel_jpeg_mode }, { "PNG", "png", filesel_png_mode }, { "TIFF", "tiff", filesel_tiff_mode }, { "CSV", "csv", filesel_csv_mode }, { "PPM", "ppm", filesel_ppm_mode } }; static FileselMode * filesel_get_mode( const char *filename ) { int i; VipsFormatClass *format; if( (format = vips_format_for_name( filename )) ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( format ); for( i = 0; i < IM_NUMBER( filesel_mode_table ); i++ ) if( strcmp( filesel_mode_table[i].name, object_class->nickname ) == 0 ) return( &filesel_mode_table[i] ); } else im_error_clear(); return( NULL ); } /* Add our image save settings to the end of a filename. filename must be * at least FILENAME_MAX characters in size. */ void filesel_add_mode( char *filename ) { FileselMode *mode; if( (mode = filesel_get_mode( filename )) ) { char ext[256]; int l = strlen( filename ); mode->mode_fn( ext ); im_snprintf( filename + l, FILENAME_MAX - l, ":%s", ext ); } } static const char * filesel_get_filter( const char *filename ) { FileselMode *mode; if( (mode = filesel_get_mode( filename )) ) return( mode->caption_filter ); return( NULL ); } static void filesel_destroy( GtkObject *object ) { Filesel *filesel; g_return_if_fail( object != NULL ); g_return_if_fail( IS_FILESEL( object ) ); filesel = FILESEL( object ); filesel_all = g_slist_remove( filesel_all, filesel ); IM_FREEF( g_free, filesel->current_dir ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* Update `space free' label. */ static void filesel_space_update( Filesel *filesel, const char *dirname ) { double sz = find_space( dirname ); if( filesel->space ) { if( sz < 0 ) set_glabel( filesel->space, _( "Unable to determine " "space free in \"%s\"." ), dirname ); else { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_append_size( &buf, sz ); vips_buf_appendf( &buf, " " ); /* Expands to (eg.) '6GB free in "/pics/tmp"' */ vips_buf_appendf( &buf, _( "free in \"%s\"" ), dirname ); set_glabel( filesel->space, "%s", vips_buf_all( &buf ) ); } } } static void * filesel_add_volume( const char *dir, Filesel *filesel ) { char buf[FILENAME_MAX]; im_strncpy( buf, dir, FILENAME_MAX ); path_expand( buf ); gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( filesel->chooser ), buf, NULL ); return( NULL ); } static void filesel_suffix_to_glob( const char *suffix, VipsBuf *patt ) { int i; char ch; vips_buf_appends( patt, "*" ); for( i = 0; (ch = suffix[i]); i++ ) { if( isalpha( ch ) ) { vips_buf_appends( patt, "[" ); vips_buf_appendf( patt, "%c", toupper( ch ) ); vips_buf_appendf( patt, "%c", tolower( ch ) ); vips_buf_appends( patt, "]" ); } else vips_buf_appendf( patt, "%c", ch ); } } /* Make a shell glob from a filetype. */ void filesel_make_patt( FileselFileType *type, VipsBuf *patt ) { int i; /* Only use {} braces if there's more than one suffix to match. */ if( type->suffixes[1] ) vips_buf_appends( patt, "{" ); for( i = 0; type->suffixes[i]; i++ ) { if( i > 0 ) vips_buf_appends( patt, "," ); filesel_suffix_to_glob( type->suffixes[i], patt ); } if( type->suffixes[1] ) vips_buf_appends( patt, "}" ); } static char * filesel_get_dir( Filesel *filesel ) { return( gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( filesel->chooser ) ) ); } static void filesel_dir_enter( Filesel *filesel ) { char *dir = filesel_get_dir( filesel ); if( !filesel->current_dir || (dir && strcmp( filesel->current_dir, dir ) != 0) ) { filesel_space_update( filesel, dir ); if( !filesel->start_name ) IM_SETSTR( filesel_last_dir, dir ); filesel->current_dir = dir; dir = NULL; } g_free( dir ); } /* New dir entered signal. */ static void filesel_current_folder_changed_cb( GtkWidget *widget, gpointer data ) { filesel_dir_enter( FILESEL( data ) ); } /* Update file info display. */ static void filesel_info_update( Filesel *filesel, const char *name ) { if( filesel->info ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); get_image_info( &buf, name ); set_glabel( filesel->info, "%s", vips_buf_firstline( &buf ) ); } } int filesel_get_filetype( Filesel *filesel ) { int type; GtkFileFilter *filter; type = filesel->default_type; if( filesel->chooser && (filter = gtk_file_chooser_get_filter( GTK_FILE_CHOOSER( filesel->chooser ) )) ) { int i; for( i = 0; filesel->filter[i]; i++ ) if( filter == filesel->filter[i] ) break; g_assert( filesel->filter[i] ); type = i; } #ifdef DEBUG printf( "filesel_get_filetype: %d\n", type ); #endif /*DEBUG*/ return( type ); } /* Find the index of the type which matches this filename. */ static int filesel_find_file_type( FileselFileType **type, const char *filename ) { int i, j; for( i = 0; type[i]; i++ ) for( j = 0; type[i]->suffixes[j]; j++ ) if( is_casepostfix( type[i]->suffixes[j], filename ) ) return( i ); return( -1 ); } static void filesel_set_filter( Filesel *filesel, GtkFileFilter *filter ) { #ifdef DEBUG printf( "filesel_set_filter: %p\n", filter ); #endif /*DEBUG*/ g_assert( filter ); gtk_file_chooser_set_filter( GTK_FILE_CHOOSER( filesel->chooser ), filter ); } static void filesel_set_filetype_from_filename( Filesel *filesel, const char *name ) { int type; int i; char *p; /* If we're showing "all", any filename is OK, so don't change the file * type. */ type = filesel_get_filetype( filesel ); if( type == filesel->ntypes - 1 ) return; /* If we've not got a sensible filename, don't bother. */ if( (p = strrchr( name, G_DIR_SEPARATOR )) && strspn( p + 1, " \n\t" ) == strlen( p + 1 ) ) return; if( (i = filesel_find_file_type( filesel->type, name )) >= 0 ) filesel_set_filter( filesel, filesel->filter[i] ); else /* No match, or no suffix. Set the last type (should be "All"). */ filesel_set_filter( filesel, filesel->filter[filesel->ntypes - 1] ); } gboolean filesel_set_filename( Filesel *filesel, const char *name ) { char buf[FILENAME_MAX]; if( !is_valid_filename( name ) ) return( FALSE ); im_strncpy( buf, name, FILENAME_MAX ); path_expand( buf ); #ifdef DEBUG printf( "filesel_set_filename: %s\n", buf ); #endif /*DEBUG*/ /* set_filename() will only select existing files, we need to be able * to set any filename (eg. for increment filename), so we have to * set_current_name() as well. */ gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( filesel->chooser ), buf ); if( filesel->save ) gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER( filesel->chooser ), im_skip_dir( buf ) ); filesel->start_name = TRUE; /* We have to set this after setting the filename. */ filesel_set_filetype_from_filename( filesel, buf ); return( TRUE ); } /* Read the filename out ... test for sanity. */ char * filesel_get_filename( Filesel *filesel ) { char *name; char tmp[FILENAME_MAX]; name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( filesel->chooser ) ); #ifdef DEBUG printf( "filesel_get_filename: %s\n", name ); #endif /*DEBUG*/ if( !name ) { error_top( _( "Bad filename." ) ); error_sub( _( "No file selected." ) ); return( NULL ); } if( !is_valid_filename( name ) ) { g_free( name ); return( NULL ); } /* Rewrite to compact form, eg. "$HOME/fred". */ im_strncpy( tmp, name, FILENAME_MAX ); path_compact( tmp ); g_free( name ); return( g_strdup( tmp ) ); } /* Get filename multi ... map over the selected filenames. */ void * filesel_map_filename_multi( Filesel *filesel, FileselMapFn fn, void *a, void *b ) { GSList *names = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER( filesel->chooser ) ); GSList *p; for( p = names; p; p = p->next ) { char *filename = (char *) p->data; char tmp[FILENAME_MAX]; void *res; im_strncpy( tmp, filename, FILENAME_MAX ); path_compact( tmp ); if( (res = fn( filesel, tmp, a, b )) ) { IM_FREEF( slist_free_all, names ); return( res ); } } IM_FREEF( slist_free_all, names ); return( NULL ); } /* New file selected signal. */ static void filesel_selection_changed_cb( GtkWidget *widget, gpointer data ) { Filesel *filesel = FILESEL( data ); char *filename; #ifdef DEBUG printf( "filesel_selection_changed_cb: %s\n", NN( IWINDOW( filesel )->title ) ); #endif /*DEBUG*/ if( (filename = filesel_get_filename( filesel )) ) { #ifdef DEBUG printf( "filesel_selection_changed_cb: %s - \"%s\"\n", NN( IWINDOW( filesel )->title ), filename ); #endif /*DEBUG*/ filesel_info_update( filesel, filename ); g_free( filename ); } } static void filesel_file_activated_cb( GtkWidget *widget, gpointer data ) { idialog_done_trigger( IDIALOG( data ), 0 ); } /* Increment filename on OK. */ static void filesel_auto_incr_cb( GtkWidget *tog, Filesel *filesel ) { filesel->incr = GTK_TOGGLE_BUTTON( tog )->active; if( filesel->incr ) idialog_set_pinup( IDIALOG( filesel ), TRUE ); } static void filesel_update_preview_cb( GtkFileChooser *chooser, Filesel *filesel ) { char *filename; if( (filename = gtk_file_chooser_get_preview_filename( GTK_FILE_CHOOSER( filesel->chooser ) )) ) { preview_set_filename( filesel->preview, filename ); g_free( filename ); } } static GtkFileFilter * file_filter_from_file_type( FileselFileType *type ) { GtkFileFilter *filter; int j; filter = gtk_file_filter_new(); gtk_file_filter_set_name( filter, _( type->name ) ); if( type->suffixes[0] ) for( j = 0; type->suffixes[j]; j++ ) { char txt[FILENAME_MAX]; VipsBuf buf = VIPS_BUF_STATIC( txt ); filesel_suffix_to_glob( type->suffixes[j], &buf ); gtk_file_filter_add_pattern( filter, vips_buf_all( &buf ) ); } else /* No suffix list means any suffix allowed. */ gtk_file_filter_add_pattern( filter, "*" ); return( filter ); } static void filesel_add_filter( Filesel *filesel, FileselFileType *type, int i ) { filesel->filter[i] = file_filter_from_file_type( type ); #ifdef DEBUG printf( "filesel_add_filter: %p (%d)\n", filesel->filter[i], i ); #endif /*DEBUG*/ gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( filesel->chooser ), filesel->filter[i] ); if( i == filesel->default_type ) filesel_set_filter( filesel, filesel->filter[i] ); } static void filesel_build( GtkWidget *widget ) { Filesel *filesel = FILESEL( widget ); iDialog *idlg = IDIALOG( widget ); int i; FileselFileType *type; GtkWidget *vb; GtkWidget *tog; #ifdef DEBUG printf( "filesel_build: %s\n", NN( IWINDOW( filesel )->title ) ); #endif /*DEBUG*/ /* Call all builds in superclasses. */ if( IWINDOW_CLASS( parent_class )->build ) IWINDOW_CLASS( parent_class )->build( widget ); filesel->chooser = gtk_file_chooser_widget_new( filesel->save ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN ); gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( filesel->chooser ), filesel->multi ); gtk_box_pack_start( GTK_BOX( idlg->work ), filesel->chooser, TRUE, TRUE, 0 ); gtk_widget_show( filesel->chooser ); /* Add data path to volumes. */ slist_map( PATH_SEARCH, (SListMapFn) filesel_add_volume, filesel ); /* Add all the supported file types. Add "all" to the end. */ for( i = 0; (type = filesel->type[i]); i++ ) filesel_add_filter( filesel, type, i ); filesel_add_filter( filesel, &filesel_allfile_type, i ); /* Spot changes. */ gtk_signal_connect( GTK_OBJECT( filesel->chooser ), "current-folder-changed", GTK_SIGNAL_FUNC( filesel_current_folder_changed_cb ), filesel ); gtk_signal_connect( GTK_OBJECT( filesel->chooser ), "selection-changed", GTK_SIGNAL_FUNC( filesel_selection_changed_cb ), filesel ); gtk_signal_connect( GTK_OBJECT( filesel->chooser ), "file-activated", GTK_SIGNAL_FUNC( filesel_file_activated_cb ), filesel ); /* Pack extra widgets. */ vb = gtk_vbox_new( FALSE, 6 ); gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( filesel->chooser ), vb ); gtk_widget_show( vb ); /* Space free label. */ if( filesel->save ) { filesel->space = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( filesel->space ), 0, 0.5 ); gtk_box_pack_start( GTK_BOX( vb ), filesel->space, FALSE, FALSE, 0 ); gtk_widget_show( filesel->space ); } /* File info label. */ if( !filesel->save ) { filesel->info = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( filesel->info ), 0, 0.5 ); gtk_box_pack_start( GTK_BOX( vb ), filesel->info, FALSE, FALSE, 0 ); gtk_widget_show( filesel->info ); } /* Auto-increment toggle. */ if( filesel->save ) { tog = gtk_check_button_new_with_label( _( "Increment filename" ) ); gtk_signal_connect( GTK_OBJECT( tog ), "toggled", GTK_SIGNAL_FUNC( filesel_auto_incr_cb ), filesel ); gtk_box_pack_start( GTK_BOX( vb ), tog, FALSE, FALSE, 0 ); gtk_widget_show( tog ); set_tooltip( tog, _( "After Save, add 1 to the last number in the " "file name" ) ); } if( filesel->imls ) { filesel->preview = preview_new(); gtk_file_chooser_set_preview_widget( GTK_FILE_CHOOSER( filesel->chooser ), GTK_WIDGET( filesel->preview ) ); gtk_signal_connect( GTK_OBJECT( filesel->chooser ), "update-preview", GTK_SIGNAL_FUNC( filesel_update_preview_cb ), filesel ); gtk_widget_show( GTK_WIDGET( filesel->preview ) ); gtk_file_chooser_set_preview_widget_active( GTK_FILE_CHOOSER( filesel->chooser ), TRUE ); } if( filesel_last_dir ) gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( filesel->chooser ), filesel_last_dir ); /* Save boxes can be much smaller. */ if( !filesel->save ) gtk_window_set_default_size( GTK_WINDOW( filesel ), 600, 500 ); } static void filesel_class_init( FileselClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; iWindowClass *iwindow_class = (iWindowClass *) class; object_class->destroy = filesel_destroy; iwindow_class->build = filesel_build; parent_class = g_type_class_peek_parent( class ); } /* Increment filename. If there's no number there now, assume zero. */ static void filesel_increment_filename( Filesel *filesel ) { char *filename; if( (filename = filesel_get_filename( filesel )) ) { char name[FILENAME_MAX]; im_strncpy( name, filename, FILENAME_MAX ); g_free( filename ); increment_filename( name ); (void) filesel_set_filename( filesel, name ); } } static void * filesel_refresh( Filesel *filesel ) { char *dir; if( (dir = gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER( filesel->chooser ) )) ) { gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( filesel->chooser ), dir ); g_free( dir ); } return( NULL ); } /* There may be a new file ... ask all fsb's to refresh. */ void filesel_refresh_all( void ) { (void) slist_map( filesel_all, (SListMapFn) filesel_refresh, NULL ); } static void filesel_init( Filesel *filesel ) { int i; #ifdef DEBUG printf( "filesel_init: %s\n", NN( IWINDOW( filesel )->title ) ); #endif /*DEBUG*/ filesel->chooser = NULL; filesel->space = NULL; filesel->info = NULL; filesel->preview = NULL; for( i = 0; i < FILESEL_MAX_FILTERS; i++ ) filesel->filter[i] = NULL; filesel->incr = FALSE; filesel->imls = FALSE; filesel->save = FALSE; filesel->multi = FALSE; filesel->start_name = FALSE; filesel->type = NULL; filesel->default_type = 0; filesel->type_pref = NULL; filesel->current_dir = NULL; filesel->done_cb = NULL; filesel->client = NULL; idialog_set_callbacks( IDIALOG( filesel ), iwindow_true_cb, NULL, NULL, NULL ); idialog_set_pinup( IDIALOG( filesel ), TRUE ); idialog_set_nosep( IDIALOG( filesel ), TRUE ); idialog_set_button_focus( IDIALOG( filesel ), FALSE ); idialog_set_help_tag( IDIALOG( filesel ), "sec:loadsave" ); filesel_all = g_slist_prepend( filesel_all, filesel ); } GtkType filesel_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Filesel", sizeof( Filesel ), sizeof( FileselClass ), (GtkClassInitFunc) filesel_class_init, (GtkObjectInitFunc) filesel_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_IDIALOG, &info ); } return( type ); } GtkWidget * filesel_new( void ) { Filesel *filesel = (Filesel *) gtk_type_new( TYPE_FILESEL ); iwindow_set_size_prefs( IWINDOW( filesel ), "FILESEL_WINDOW_WIDTH", "FILESEL_WINDOW_HEIGHT" ); return( GTK_WIDGET( filesel ) ); } void filesel_set_done( Filesel *filesel, iWindowFn done_cb, void *client ) { filesel->done_cb = done_cb; filesel->client = client; } /* Back from the user function ... unset the hourglass, and update. */ static void filesel_trigger2( void *sys, iWindowResult result ) { iWindowSusp *susp = (iWindowSusp *) sys; Filesel *filesel = FILESEL( susp->client ); progress_end(); /* If this is a save, assume that there is now a new file, * and ask all fsb's to update. */ if( filesel->save && result != IWINDOW_ERROR ) filesel_refresh_all(); if( result != IWINDOW_YES ) { /* Failure ... bomb out. */ iwindow_susp_return( susp, result ); return; } /* Increment the filename, if required. */ if( filesel->incr ) { filesel_increment_filename( filesel ); filesel_refresh( filesel ); } /* Success! */ iwindow_susp_return( susp, result ); } /* Start of user done ... shut down our suspension, and set the hglass. */ static void filesel_trigger( Filesel *filesel, iWindow *iwnd, iWindowNotifyFn nfn, void *sys ) { /* Suspend the callback for a bit. */ iWindowSusp *susp = iwindow_susp_new( NULL, iwnd, filesel, nfn, sys ); /* If there's a filetype pref, update it. */ if( filesel->type_pref ) prefs_set( filesel->type_pref, "%d", filesel_get_filetype( filesel ) ); progress_begin(); filesel->done_cb( IWINDOW( filesel ), filesel->client, filesel_trigger2, susp ); } static void filesel_prefs_ok_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( client ); /* Force a recalc, in case we've changed the autorecalc * settings. Also does a scan on any widgets. */ symbol_recalculate_all_force( TRUE ); filesel_trigger( filesel, iwnd, nfn, sys ); } static void filesel_prefs( Filesel *filesel, iWindow *iwnd, const char *caption_filter, iWindowNotifyFn nfn, void *sys ) { Prefs *prefs; if( !(prefs = prefs_new( caption_filter )) ) { nfn( sys, IWINDOW_ERROR ); return; } /* Expands to (eg.) "TIFF Save Preferences". */ iwindow_set_title( IWINDOW( prefs ), _( "%s Save Preferences" ), caption_filter ); iwindow_set_parent( IWINDOW( prefs ), GTK_WIDGET( iwnd ) ); idialog_set_callbacks( IDIALOG( prefs ), iwindow_true_cb, NULL, NULL, filesel ); idialog_add_ok( IDIALOG( prefs ), filesel_prefs_ok_cb, GTK_STOCK_SAVE ); idialog_set_notify( IDIALOG( prefs ), nfn, sys ); iwindow_build( IWINDOW( prefs ) ); gtk_widget_show( GTK_WIDGET( prefs ) ); } /* We have a filename and it's OK to overwrite. Is it a type for which we have * to offer preferences? */ static void filesel_yesno_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( client ); char *filename; const char *caption_filter; if( filesel->save ) { if( !(filename = filesel_get_filename( filesel )) ) nfn( sys, IWINDOW_ERROR ); else { if( (caption_filter = filesel_get_filter( filename )) ) filesel_prefs( filesel, iwnd, caption_filter, nfn, sys ); else filesel_trigger( filesel, iwnd, nfn, sys ); g_free( filename ); } } else filesel_trigger( filesel, iwnd, nfn, sys ); } static void filesel_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); char *filename; #ifdef DEBUG printf( "filesel_done\n" ); #endif /*DEBUG*/ if( !(filename = filesel_get_filename( filesel )) ) nfn( sys, IWINDOW_ERROR ); else if( isdir( "%s", filename ) ) { nfn( sys, IWINDOW_NO ); } else { /* File exists and we are saving? Do a yesno before we carry on. */ if( filesel->save && existsf( "%s", filename ) ) { box_yesno( GTK_WIDGET( filesel ), filesel_yesno_cb, iwindow_true_cb, filesel, nfn, sys, _( "Overwrite" ), _( "Overwrite file?" ), _( "File \"%s\" exists. " "OK to overwrite?" ), filename ); } else /* Just call the user function directly. */ filesel_yesno_cb( iwnd, filesel, nfn, sys ); g_free( filename ); } } void filesel_set_flags( Filesel *filesel, gboolean imls, gboolean save ) { filesel->imls = imls; filesel->save = save; idialog_add_ok( IDIALOG( filesel ), filesel_done_cb, save ? GTK_STOCK_SAVE : GTK_STOCK_OPEN ); } void filesel_set_filetype( Filesel *filesel, FileselFileType **type, int default_type ) { /* Reset the widget, if it's there. */ if( filesel->chooser ) filesel_set_filter( filesel, filesel->filter[default_type] ); filesel->type = type; filesel->ntypes = array_len( (void **) type ); filesel->default_type = default_type; } void filesel_set_filetype_pref( Filesel *filesel, const char *type_pref ) { filesel->type_pref = type_pref; } void filesel_set_multi( Filesel *filesel, gboolean multi ) { filesel->multi = multi; } ================================================ FILE: src/filesel.h ================================================ /* Declarations for ifileselect.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* How we define a file type. Pass a NULL-terminated array of pointers * to these puppies to gtk_file_selection2_set_file_types(). * gtk_file_selection2_set_file_types() makes a copy of the data itself, * so you can free if you want. */ typedef struct _FileselFileType { /* Descriptive name for this file type. Eg: * "TIFF image file (*.tif; *.tiff)" */ const char *name; /* NULL-terminated array of suffixes identifying this * file type. Put the default first. Eg: * { ".tif", ".tiff", NULL }, or * { ".htm", ".html", NULL } */ const char **suffixes; } FileselFileType; /* Basic types. */ extern FileselFileType filesel_wfile_type, filesel_rfile_type, filesel_mfile_type, filesel_cfile_type, filesel_xfile_type, filesel_dfile_type, filesel_ifile_type; /* Suffix sets we support. */ extern FileselFileType *filesel_type_definition[]; extern FileselFileType *filesel_type_workspace[]; extern FileselFileType *filesel_type_matrix[]; extern FileselFileType **filesel_type_image; extern FileselFileType **filesel_type_mainw; extern FileselFileType **filesel_type_any; /* Subclass off gtkfilesel2.c to make one of our fileselectors. */ #define TYPE_FILESEL (filesel_get_type()) #define FILESEL( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_FILESEL, Filesel )) #define FILESEL_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_FILESEL, FileselClass )) #define IS_FILESEL( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FILESEL )) #define IS_FILESEL_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FILESEL )) /* Must be enough. */ #define FILESEL_MAX_FILTERS (100) typedef struct _Filesel { iDialog parent; /* Widgets. */ GtkWidget *chooser; /* Filechooser widget */ GtkWidget *space; /* Space available */ GtkWidget *info; /* File info */ Preview *preview; /* Selected file preview */ GtkFileFilter *filter[FILESEL_MAX_FILTERS]; /* State. */ gboolean incr; /* True for increment filename */ gboolean imls; /* True if this is image load/save */ gboolean save; /* True if this is a save dialog */ gboolean multi; /* Multiple-select */ gboolean start_name; /* True if we have a suggested name */ FileselFileType **type; /* Allowable types for this filesel */ int ntypes; int default_type; const char *type_pref; /* Pref to set on type change */ /* Last dir we entered. Used to stop dir_changed being emitted too * often. */ char *current_dir; iWindowFn done_cb; /* On OK */ void *client; } Filesel; typedef struct _FileselClass { iDialogClass parent_class; /* My methods. */ } FileselClass; void filesel_startup( void ); gboolean is_file_type( const FileselFileType *type, const char *filename ); typedef void *(*FileselMapFn)( Filesel *, const char *, void *, void * ); void filesel_add_mode( char *filename ); GtkType filesel_get_type( void ); GtkWidget *filesel_new( void ); gboolean filesel_set_filename( Filesel *filesel, const char *name ); char *filesel_get_filename( Filesel *filesel ); void *filesel_map_filename_multi( Filesel *filesel, FileselMapFn fn, void *a, void *b ); void filesel_set_done( Filesel *filesel, iWindowFn done_cb, void *client ); void filesel_set_filetype( Filesel *filesel, FileselFileType **type, int default_type ); void filesel_set_filetype_pref( Filesel *filesel, const char *type_pref ); int filesel_get_filetype( Filesel *filesel ); void filesel_make_patt( FileselFileType *type, VipsBuf *patt ); void filesel_set_flags( Filesel *filesel, gboolean imls, gboolean save ); void filesel_set_multi( Filesel *filesel, gboolean multi ); ================================================ FILE: src/floatwindow.c ================================================ /* abstract base class for floatwindow / plotwindow etc. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static iWindowClass *parent_class = NULL; static void floatwindow_popdown( GtkWidget *widget ) { Floatwindow *floatwindow = FLOATWINDOW( widget ); Model *model = floatwindow->model; #ifdef DEBUG printf( "floatwindow_popdown\n" ); #endif /*DEBUG*/ /* We have to note position/size in popdown rather than destroy, since * the widgets have to all still be extant. */ /* Note position/size for later reuse. */ model->window_width = GTK_WIDGET( floatwindow )->allocation.width; model->window_height = GTK_WIDGET( floatwindow )->allocation.height; gdk_window_get_root_origin( gtk_widget_get_toplevel( GTK_WIDGET( floatwindow ) )->window, &model->window_x, &model->window_y ); IWINDOW_CLASS( parent_class )->popdown( widget ); } static void floatwindow_build( GtkWidget *widget ) { Floatwindow *floatwindow = FLOATWINDOW( widget ); Model *model = floatwindow->model; IWINDOW_CLASS( parent_class )->build( widget ); /* Must be set with floatmodel_link before build. */ g_assert( floatwindow->model ); /* Position and size to restore? Come here after parent build, so we * can override any default settings from there. */ if( model->window_width != -1 ) { GdkScreen *screen = gtk_widget_get_screen( GTK_WIDGET( floatwindow ) ); int screen_width = gdk_screen_get_width( screen ); int screen_height = gdk_screen_get_height( screen ); /* We need to clip x/y against the desktop size ... we may be * loading a workspace made on a machine with a big screen on * a machine with a small screen. FIXME ... we could only clip if the window will be completely off the screen? ie. ignore iimage->window_width etc. */ int window_x = IM_CLIP( 0, model->window_x, screen_width - model->window_width ); int window_y = IM_CLIP( 0, model->window_y, screen_height - model->window_height ); int window_width = IM_MIN( model->window_width, screen_width ); int window_height = IM_MIN( model->window_height, screen_height ); gtk_widget_set_uposition( GTK_WIDGET( floatwindow ), window_x, window_y ); gtk_window_set_default_size( GTK_WINDOW( floatwindow ), window_width, window_height ); } } static void floatwindow_class_init( FloatwindowClass *class ) { iWindowClass *iwindow_class = (iWindowClass *) class; parent_class = g_type_class_peek_parent( class ); iwindow_class->build = floatwindow_build; iwindow_class->popdown = floatwindow_popdown; /* Hmm, this rather negates the point of this class. If we make plot * and image windows transient for the main window, we don't get * maximise buttons :-( (on gnome and win anyway). * * Keep this class around for now, maybe it'll still be useful. */ iwindow_class->transient = FALSE; /* Create signals. */ /* Init methods. */ } static void floatwindow_init( Floatwindow *floatwindow ) { floatwindow->model = NULL; } GType floatwindow_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( FloatwindowClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) floatwindow_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Floatwindow ), 32, /* n_preallocs */ (GInstanceInitFunc) floatwindow_init, }; type = g_type_register_static( TYPE_IWINDOW, "Floatwindow", &info, 0 ); } return( type ); } void floatwindow_link( Floatwindow *floatwindow, Model *model ) { floatwindow->model = model; destroy_if_destroyed( G_OBJECT( floatwindow ), G_OBJECT( model ), (DestroyFn) gtk_widget_destroy ); } ================================================ FILE: src/floatwindow.h ================================================ /* abstract base class for imageview / plotwindow etc. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_FLOATWINDOW (floatwindow_get_type()) #define FLOATWINDOW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_FLOATWINDOW, Floatwindow )) #define FLOATWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_FLOATWINDOW, FloatwindowClass )) #define IS_FLOATWINDOW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FLOATWINDOW )) #define IS_FLOATWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FLOATWINDOW )) typedef struct _Floatwindow { iWindow parent_class; /* Model stuff here. */ Model *model; } Floatwindow; typedef struct _FloatwindowClass { iWindowClass parent_class; /* My methods. */ } FloatwindowClass; GtkType floatwindow_get_type( void ); void floatwindow_link( Floatwindow *floatwindow, Model *model ); ================================================ FILE: src/fontname.c ================================================ /* an input fontname ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void fontname_finalize( GObject *gobject ) { Fontname *fontname = FONTNAME( gobject ); IM_FREE( fontname->value ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static View * fontname_view_new( Model *model, View *parent ) { return( fontnameview_new() ); } /* Members of fontname we automate. */ static ClassmodelMember fontname_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( iObject, caption ) }, { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Fontname, value ) } }; static void fontname_class_init( FontnameClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = fontname_finalize; model_class->view_new = fontname_view_new; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = fontname_members; classmodel_class->n_members = IM_NUMBER( fontname_members ); } static void fontname_init( Fontname *fontname ) { /* Overridden later. Just something sensible. */ fontname->value = NULL; IM_SETSTR( fontname->value, "Sans" ); iobject_set( IOBJECT( fontname ), CLASS_FONTNAME, NULL ); } GType fontname_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( FontnameClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) fontname_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Fontname ), 32, /* n_preallocs */ (GInstanceInitFunc) fontname_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Fontname", &info, 0 ); } return( type ); } ================================================ FILE: src/fontname.h ================================================ /* a fontname in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_FONTNAME (fontname_get_type()) #define FONTNAME( obj ) (GTK_CHECK_CAST( (obj), TYPE_FONTNAME, Fontname )) #define FONTNAME_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_FONTNAME, FontnameClass )) #define IS_FONTNAME( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FONTNAME )) #define IS_FONTNAME_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FONTNAME )) typedef struct _Fontname { Classmodel model; char *value; } Fontname; typedef struct _FontnameClass { ClassmodelClass parent_class; /* My methods. */ } FontnameClass; GType fontname_get_type( void ); ================================================ FILE: src/fontnameview.c ================================================ /* run the display for an arrow in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; static void fontnameview_link( View *view, Model *model, View *parent ) { Fontnameview *fontnameview = FONTNAMEVIEW( view ); VIEW_CLASS( parent_class )->link( view, model, parent ); if( GRAPHICVIEW( view )->sview ) gtk_size_group_add_widget( GRAPHICVIEW( view )->sview->group, fontnameview->label ); } static void fontnameview_refresh( vObject *vobject ) { Fontnameview *fontnameview = FONTNAMEVIEW( vobject ); Fontname *fontname = FONTNAME( VOBJECT( vobject )->iobject ); #ifdef DEBUG printf( "fontnameview_refresh: " ); row_name_print( HEAPMODEL( fontname )->row ); printf( "\n" ); #endif /*DEBUG*/ if( vobject->iobject->caption ) set_glabel( fontnameview->label, _( "%s:" ), vobject->iobject->caption ); if( fontname->value ) fontbutton_set_font_name( fontnameview->fontbutton, fontname->value ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void fontnameview_class_init( FontnameviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = fontnameview_refresh; view_class->link = fontnameview_link; } static void fontnameview_changed_cb( Fontbutton *fontbutton, Fontnameview *fontnameview ) { Fontname *fontname = FONTNAME( VOBJECT( fontnameview )->iobject ); const char *font_name = fontbutton_get_font_name( fontbutton ); if( strcmp( font_name, fontname->value ) != 0 ) { IM_SETSTR( fontname->value, font_name ); classmodel_update( CLASSMODEL( fontname ) ); symbol_recalculate_all(); } } static void fontnameview_init( Fontnameview *fontnameview ) { GtkWidget *hbox; #ifdef DEBUG printf( "fontnameview_init\n" ); #endif /*DEBUG*/ hbox = gtk_hbox_new( FALSE, 12 ); gtk_box_pack_start( GTK_BOX( fontnameview ), hbox, TRUE, FALSE, 0 ); fontnameview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( fontnameview->label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( fontnameview->label ), 2, 7 ); gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( fontnameview->label ), FALSE, FALSE, 2 ); fontnameview->fontbutton = fontbutton_new(); gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( fontnameview->fontbutton ), TRUE, TRUE, 0 ); g_signal_connect( fontnameview->fontbutton, "changed", G_CALLBACK( fontnameview_changed_cb ), fontnameview ); gtk_widget_show_all( GTK_WIDGET( hbox ) ); } GtkType fontnameview_get_type( void ) { static GtkType fontnameview_type = 0; if( !fontnameview_type ) { static const GtkTypeInfo info = { "Fontnameview", sizeof( Fontnameview ), sizeof( FontnameviewClass ), (GtkClassInitFunc) fontnameview_class_init, (GtkObjectInitFunc) fontnameview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; fontnameview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( fontnameview_type ); } View * fontnameview_new( void ) { Fontnameview *fontnameview = gtk_type_new( TYPE_FONTNAMEVIEW ); return( VIEW( fontnameview ) ); } ================================================ FILE: src/fontnameview.h ================================================ /* a fontname view in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_FONTNAMEVIEW (fontnameview_get_type()) #define FONTNAMEVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_FONTNAMEVIEW, Fontnameview )) #define FONTNAMEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_FONTNAMEVIEW, FontnameviewClass )) #define IS_FONTNAMEVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FONTNAMEVIEW )) #define IS_FONTNAMEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FONTNAMEVIEW )) typedef struct _Fontnameview { Graphicview parent_object; GtkWidget *label; Fontbutton *fontbutton; } Fontnameview; typedef struct _FontnameviewClass { GraphicviewClass parent_class; /* My methods. */ } FontnameviewClass; GtkType fontnameview_get_type( void ); View *fontnameview_new( void ); ================================================ FILE: src/formula.c ================================================ /* display a caption/value label pair, on a click display the formula in an * entry widget */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Our signals. */ enum { EDIT, CHANGED, ACTIVATE, ENTER, LEAVE, LAST_SIGNAL }; static GtkEventBoxClass *parent_class = NULL; static guint formula_signals[LAST_SIGNAL] = { 0 }; /* Formula needing a refresh. */ static GSList *formula_refresh_all = NULL; /* The idle we add if there are any formula needing a refresh. */ static gint formula_refresh_idle = 0; /* Unqueue a refresh. */ static void formula_refresh_unqueue( Formula *formula ) { if( formula->refresh_queued ) { formula_refresh_all = g_slist_remove( formula_refresh_all, formula ); formula->refresh_queued = FALSE; if( !formula_refresh_all ) IM_FREEF( g_source_remove, formula_refresh_idle ); } } /* Detect cancel in a text field. */ static gboolean formula_key_press_event_cb( GtkWidget *widget, GdkEventKey *ev, Formula *formula ) { gboolean handled; handled = FALSE; if( ev->keyval == GDK_Escape ) { set_gentry( formula->entry, "%s", formula->expr ); /* FIXME ... really we want to go back to the edit mode set by our environment (eg. if we're in a show_formula workspace, should stay in show formula). */ formula_set_edit( formula, FALSE ); handled = TRUE; } return( handled ); } /* Activated! */ static void formula_activate( Formula *formula ) { g_signal_emit( G_OBJECT( formula ), formula_signals[ACTIVATE], 0 ); } static void formula_activate_cb( GtkWidget *wid, Formula *formula ) { formula_activate( formula ); } /* A char has changed in the entry (we will need scanning on activate). */ static void formula_changed( Formula *formula ) { g_signal_emit( G_OBJECT( formula ), formula_signals[CHANGED], 0 ); } /* Add an edit box. */ static void formula_add_edit( Formula *formula ) { if( formula->entry_frame ) return; /* We need to use an alignment since if the left label is hidden we'll * have nothing to hold us to the right height. */ formula->entry_frame = gtk_alignment_new( 0.5, 0.5, 1, 1 ); gtk_alignment_set_padding( GTK_ALIGNMENT( formula->entry_frame ), 3, 3, 2, 2 ); gtk_box_pack_start( GTK_BOX( formula->hbox ), formula->entry_frame, TRUE, TRUE, 0 ); formula->entry = gtk_entry_new(); set_tooltip( formula->entry, _( "Press Escape to cancel edit, " "press Return to accept edit and recalculate" ) ); gtk_signal_connect( GTK_OBJECT( formula->entry ), "key_press_event", GTK_SIGNAL_FUNC( formula_key_press_event_cb ), GTK_OBJECT( formula ) ); gtk_signal_connect_object( GTK_OBJECT( formula->entry ), "changed", GTK_SIGNAL_FUNC( formula_changed ), GTK_OBJECT( formula ) ); gtk_signal_connect( GTK_OBJECT( formula->entry ), "activate", GTK_SIGNAL_FUNC( formula_activate_cb ), formula ); gtk_container_add( GTK_CONTAINER( formula->entry_frame ), formula->entry ); gtk_widget_show( formula->entry ); /* Tell everyone we are in edit mode ... used to add to resettable, * for example. */ g_signal_emit( G_OBJECT( formula ), formula_signals[EDIT], 0 ); } static void formula_refresh( Formula *formula ) { #ifdef DEBUG printf( "formula_refresh\n" ); #endif /*DEBUG*/ /* Set edit mode. */ if( formula->edit ) { formula_add_edit( formula ); gtk_widget_show( formula->entry_frame ); gtk_widget_hide( formula->right_label ); formula->changed = FALSE; } else { gtk_widget_show( formula->right_label ); IM_FREEF( gtk_widget_destroy, formula->entry ); IM_FREEF( gtk_widget_destroy, formula->entry_frame ); } /* Don't update the formula display if the user has edited the text ... * we shouldn't destroy their work. */ if( formula->entry && formula->expr && !formula->changed ) { /* Make sure we don't trigger "changed" when we zap in new * text. */ gtk_signal_handler_block_by_data( GTK_OBJECT( formula->entry ), formula ); set_gentry( formula->entry, "%s", formula->expr ); gtk_signal_handler_unblock_by_data( GTK_OBJECT( formula->entry ), formula ); } if( formula->caption ) { set_glabel( formula->left_label, _( "%s:" ), formula->caption ); gtk_widget_show( formula->left_label ); } else gtk_widget_hide( formula->left_label ); if( formula->value ) /* Just display the first line of the formula ... it can be * mutiline for class members, for example. */ set_glabel1( formula->right_label, "%s", formula->value ); if( formula->edit && formula->needs_focus ) { if( formula->expr ) gtk_editable_select_region( GTK_EDITABLE( formula->entry ), 0, -1 ); gtk_widget_grab_focus( formula->entry ); formula->needs_focus = FALSE; } } static gboolean formula_refresh_idle_cb( void ) { formula_refresh_idle = 0; while( formula_refresh_all ) { Formula *formula = FORMULA( formula_refresh_all->data ); formula_refresh_unqueue( formula ); formula_refresh( formula ); } return( FALSE ); } static void formula_refresh_queue( Formula *formula ) { if( !formula->refresh_queued ) { formula_refresh_all = g_slist_prepend( formula_refresh_all, formula ); formula->refresh_queued = TRUE; if( !formula_refresh_idle ) formula_refresh_idle = g_idle_add( (GSourceFunc) formula_refresh_idle_cb, NULL ); } } static void formula_destroy( GtkObject *object ) { Formula *formula; #ifdef DEBUG printf( "formula_destroy\n" ); #endif /*DEBUG*/ g_return_if_fail( object != NULL ); g_return_if_fail( IS_FORMULA( object ) ); /* My instance destroy stuff. */ formula = FORMULA( object ); formula_refresh_unqueue( formula ); IM_FREE( formula->caption ); IM_FREE( formula->value ); IM_FREE( formula->expr ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* Change edit mode. */ void formula_set_edit( Formula *formula, gboolean edit ) { #ifdef DEBUG printf( "formula_set_edit: %d\n", edit ); #endif /*DEBUG*/ if( formula->edit != edit ) { formula->edit = edit; formula_refresh_queue( formula ); } /* Can't have been edited yet, whichever way we're turning edit. */ formula->changed = FALSE; } /* Grab focus on next refresh. */ void formula_set_needs_focus( Formula *formula, gboolean needs_focus ) { #ifdef DEBUG printf( "formula_set_needs_focus: %d\n", needs_focus ); #endif /*DEBUG*/ if( formula->needs_focus != needs_focus ) { formula->needs_focus = needs_focus; formula_refresh_queue( formula ); } } /* Change sensitive mode. */ void formula_set_sensitive( Formula *formula, gboolean sensitive ) { #ifdef DEBUG printf( "formula_set_sensitive: %d\n", sensitive ); #endif /*DEBUG*/ if( formula->sensitive != sensitive ) { formula->sensitive = sensitive; if( !formula->sensitive ) formula_set_edit( formula, FALSE ); formula_refresh_queue( formula ); } } /* Re-read the text. TRUE if we saw a change. */ gboolean formula_scan( Formula *formula ) { gboolean changed; #ifdef DEBUG printf( "formula_scan\n" ); #endif /*DEBUG*/ changed = FALSE; /* Should be in edit mode. */ if( formula->edit && formula->entry && GTK_WIDGET_VISIBLE( formula->entry ) ) { const char *expr; /* There should be some edited text. */ expr = gtk_entry_get_text( GTK_ENTRY( formula->entry ) ); if( expr && strspn( expr, WHITESPACE ) != strlen( expr ) ) { IM_SETSTR( formula->expr, expr ); changed = TRUE; } formula_set_edit( formula, FALSE ); } return( changed ); } static gboolean formula_enter_notify_event( GtkWidget *widget, GdkEventCrossing *event ) { GtkWidget *event_widget; event_widget = gtk_get_event_widget( (GdkEvent *) event ); if( event_widget == widget && event->detail != GDK_NOTIFY_INFERIOR ) { gtk_widget_set_state( widget, GTK_STATE_PRELIGHT ); /* Tell people about our highlight change ... used to (eg.) set * flash help. */ g_signal_emit( G_OBJECT( widget ), formula_signals[ENTER], 0 ); } return( FALSE ); } static gboolean formula_leave_notify_event( GtkWidget *widget, GdkEventCrossing *event ) { GtkWidget *event_widget; event_widget = gtk_get_event_widget( (GdkEvent *) event ); if( event_widget == widget && event->detail != GDK_NOTIFY_INFERIOR ) { gtk_widget_set_state( widget, GTK_STATE_NORMAL ); /* Tell people about our highlight change ... used to (eg.) set * flash help. */ g_signal_emit( G_OBJECT( widget ), formula_signals[LEAVE], 0 ); } return( FALSE ); } /* Event in us somewhere. */ static gboolean formula_button_press_event( GtkWidget *widget, GdkEventButton *event ) { gboolean handled = FALSE; if( event->type == GDK_BUTTON_PRESS ) { Formula *formula = FORMULA( widget ); if( event->button == 1 && formula->sensitive ) { if( !formula->edit ) { formula_set_edit( formula, TRUE ); formula_set_needs_focus( formula, TRUE ); } handled = TRUE; } } return( handled ); } static void formula_real_changed( Formula *formula ) { #ifdef DEBUG printf( "formula_real_changed\n" ); #endif /*DEBUG*/ formula->changed = TRUE; } static void formula_class_init( FormulaClass *class ) { GtkObjectClass *gobject_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->destroy = formula_destroy; widget_class->enter_notify_event = formula_enter_notify_event; widget_class->leave_notify_event = formula_leave_notify_event; widget_class->button_press_event = formula_button_press_event; /* Create signals. */ formula_signals[EDIT] = g_signal_new( "edit", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( FormulaClass, changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); formula_signals[CHANGED] = g_signal_new( "changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( FormulaClass, changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); formula_signals[ACTIVATE] = g_signal_new( "activate", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( FormulaClass, activate ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); formula_signals[ENTER] = g_signal_new( "enter", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( FormulaClass, enter ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); formula_signals[LEAVE] = g_signal_new( "leave", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( FormulaClass, leave ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); /* Init methods. */ class->changed = formula_real_changed; } static void formula_init( Formula *formula ) { /* How annoying! To avoid vertical resizes on edit/view toggles we * need to add differing amounts of padding to the label depending on * the theme. FIXME ... get this from the style somehow */ #ifdef OS_WIN32 /* with either wimp theme or gtk default. */ const int vpadding = 7; #else /*!OS_WIN32*/ /* clearlooks */ const int vpadding = 8; #endif /*OS_WIN32*/ formula->caption = NULL; formula->value = NULL; formula->expr = NULL; formula->edit = FALSE; formula->sensitive = TRUE; formula->changed = FALSE; formula->refresh_queued = FALSE; formula->needs_focus = FALSE; formula->entry_frame = NULL; gtk_widget_add_events( GTK_WIDGET( formula ), GDK_POINTER_MOTION_HINT_MASK ); formula->hbox = gtk_hbox_new( FALSE, 12 ); gtk_container_add( GTK_CONTAINER( formula ), formula->hbox ); gtk_widget_show( formula->hbox ); formula->left_label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( formula->left_label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( formula->left_label ), 2, vpadding ); gtk_box_pack_start( GTK_BOX( formula->hbox ), formula->left_label, FALSE, FALSE, 2 ); gtk_widget_show( formula->left_label ); formula->right_label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( formula->right_label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( formula->right_label ), 7, vpadding ); gtk_box_pack_start( GTK_BOX( formula->hbox ), formula->right_label, TRUE, TRUE, 0 ); gtk_widget_show( formula->right_label ); } GtkType formula_get_type( void ) { static GtkType formula_type = 0; if( !formula_type ) { static const GtkTypeInfo formula_info = { "Formula", sizeof( Formula ), sizeof( FormulaClass ), (GtkClassInitFunc) formula_class_init, (GtkObjectInitFunc) formula_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; formula_type = gtk_type_unique( GTK_TYPE_EVENT_BOX, &formula_info ); } return( formula_type ); } Formula * formula_new( void ) { Formula *formula = gtk_type_new( TYPE_FORMULA ); return( formula ); } void formula_set_caption( Formula *formula, const char *caption ) { if( !caption && formula->caption ) { IM_FREE( formula->caption ); formula_refresh_queue( formula ); } else if( caption && (!formula->caption || strcmp( caption, formula->caption ) != 0) ) { IM_SETSTR( formula->caption, caption ); formula_refresh_queue( formula ); } } void formula_set_value_expr( Formula *formula, const char *value, const char *expr ) { #ifdef DEBUG printf( "formula_set_value_expr: value=\"%s\", expr=\"%s\"\n", value, expr ); #endif /*DEBUG*/ if( value && (!formula->value || strcmp( value, formula->value ) != 0) ) { IM_SETSTR( formula->value, value ); formula_refresh_queue( formula ); } if( expr && (!formula->expr || strcmp( expr, formula->expr ) != 0) ) { IM_SETSTR( formula->expr, expr ); formula_refresh_queue( formula ); } } ================================================ FILE: src/formula.h ================================================ /* display a caption/value label pair, on a click display the formula */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_FORMULA (formula_get_type()) #define FORMULA( obj ) (GTK_CHECK_CAST( (obj), \ TYPE_FORMULA, Formula )) #define FORMULA_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_FORMULA, FormulaClass )) #define IS_FORMULA( obj ) (GTK_CHECK_TYPE( (obj), TYPE_FORMULA )) #define IS_FORMULA_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_FORMULA )) typedef struct _Formula { GtkEventBox parent_object; /* State. */ char *caption; char *value; char *expr; gboolean edit; /* In edit mode */ gboolean sensitive; /* Flick to edit on click */ gboolean changed; /* ->entry changed since we set it */ gboolean refresh_queued; /* Awaiting refresh */ gboolean needs_focus; /* Grab focus on refresh */ /* Widgets. */ GtkWidget *hbox; /* Container for our stuff */ GtkWidget *left_label; /* Caption label */ GtkWidget *right_label; /* Display value here */ GtkWidget *entry_frame; /* Frame edit text with this */ GtkWidget *entry; /* Edit formula here */ } Formula; typedef struct _FormulaClass { GtkEventBoxClass parent_class; /* My methods. */ void (*edit)( Formula * ); /* Formula has flicked to edit mode */ void (*changed)( Formula * ); /* Formula change */ void (*activate)( Formula * ); /* Pressed "Enter" key in formula */ void (*enter)( Formula * ); /* Highlight change */ void (*leave)( Formula * ); /* on eg. mouse enter/exit */ } FormulaClass; void formula_set_edit( Formula *formula, gboolean edit ); void formula_set_sensitive( Formula *formula, gboolean sensitive ); void formula_set_needs_focus( Formula *formula, gboolean needs_focus ); gboolean formula_scan( Formula *formula ); GType formula_get_type( void ); Formula *formula_new( void ); void formula_set_caption( Formula *formula, const char *caption ); void formula_set_value_expr( Formula *formula, const char *value, const char *expr ); ================================================ FILE: src/graphicview.c ================================================ /* run the display for a graphic in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static void graphicview_link( View *view, Model *model, View *parent ) { Graphicview *graphicview = GRAPHICVIEW( view ); View *v; VIEW_CLASS( parent_class )->link( view, model, parent ); /* Find the enclosing subcolumnview. */ for( v = parent; v && !IS_SUBCOLUMNVIEW( v ); v = v->parent ) ; if( v ) graphicview->sview = SUBCOLUMNVIEW( v ); } static void graphicview_class_init( GraphicviewClass *class ) { ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); view_class->link = graphicview_link; } static void graphicview_init( Graphicview *graphicview ) { graphicview->sview = NULL; } GtkType graphicview_get_type( void ) { static GtkType graphicview_type = 0; if( !graphicview_type ) { static const GtkTypeInfo sinfo = { "Graphicview", sizeof( Graphicview ), sizeof( GraphicviewClass ), (GtkClassInitFunc) graphicview_class_init, (GtkObjectInitFunc) graphicview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; graphicview_type = gtk_type_unique( TYPE_VIEW, &sinfo ); } return( graphicview_type ); } ================================================ FILE: src/graphicview.h ================================================ /* abstract base class for graphic views, ie. things we can display as part of * the graphic component of a rhsview */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_GRAPHICVIEW (graphicview_get_type()) #define GRAPHICVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_GRAPHICVIEW, Graphicview )) #define GRAPHICVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_GRAPHICVIEW, GraphicviewClass )) #define IS_GRAPHICVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_GRAPHICVIEW )) #define IS_GRAPHICVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_GRAPHICVIEW )) typedef struct _Graphicview { View view; /* My instance vars. */ Subcolumnview *sview; /* Enclosing subc. */ } Graphicview; typedef struct _GraphicviewClass { ViewClass parent_class; /* My methods. */ } GraphicviewClass; GtkType graphicview_get_type( void ); ================================================ FILE: src/graphwindow.c ================================================ /* display workspaces with graphviz */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* TODO Don't generate DOT in a file, build the graph ourselves. We'd need some sort of graph hash thing to avoid relayouts. Or perhaps a workspace hash? A hash by column? After layout, don't render to a file, instead draw directly to the output window with cairo. Copy/paste code from the png:cairo render. Have some simple culling stuff to only render visible parts of the graph. As well as "view workspace as graph", have a "view column as graph" item. Don't show nodes which are not inside this workspace. Possibly add graph editing, or at least edge highlighting as you mouse. Let columns be collapsed / expanded, and start the view with columns collapsed if there's more than 1. This might be easier if columns were separate scopes, I suppose. Do we should dynamic dependencies? How about (A2 = untitled."A1") ? */ /* #define DEBUG */ #include "ip.h" #ifdef HAVE_LIBGVC static FloatwindowClass *parent_class = NULL; static int graph_write_cluster_index = 0; static void * graph_write_row_child( Link *link, VipsBuf *buf ) { if( link->child->expr && link->child->expr->row ) { vips_buf_appendf( buf, "\t\t%s -> %s;\n", IOBJECT( link->child )->name, IOBJECT( link->parent )->name ); } return( NULL ); } static void * graph_write_row( Row *row, VipsBuf *buf ) { if( row->sym ) slist_map( row->sym->topchildren, (SListMapFn) graph_write_row_child, buf ); return( NULL ); } static void * graph_write_column( Column *col, VipsBuf *buf ) { vips_buf_appendf( buf, "\tsubgraph cluster_%d {\n", graph_write_cluster_index++ ); vips_buf_appendf( buf, "\t\tlabel = \"%s", IOBJECT( col )->name ); if( IOBJECT( col )->caption ) vips_buf_appendf( buf, " - %s", IOBJECT( col )->caption ); vips_buf_appends( buf, "\"\n" ); vips_buf_appends( buf, "\t\tstyle=filled;\n" ); vips_buf_appends( buf, "\t\tcolor=lightgrey;\n" ); vips_buf_appends( buf, "\t\tnode [style=filled,color=white];\n" ); (void) column_map( col, (row_map_fn) graph_write_row, buf, NULL ); vips_buf_appends( buf, "\t}\n" ); return( NULL ); } /* Generate the workspace in dot format. */ static void graph_write_dot( Workspace *ws, VipsBuf *buf ) { graph_write_cluster_index = 0; vips_buf_appends( buf, "digraph G {\n" ); workspace_map_column( ws, (column_map_fn) graph_write_column, buf ); vips_buf_appends( buf, "}\n" ); } /* Print the workspace in dot format. Display with something like: * $ dot test1.dot -o test1.png -Tpng:cairo -v * $ eog test1.png */ void graph_write( Workspace *ws ) { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_write_dot( ws, &buf ); printf( "%s", vips_buf_all( &buf ) ); } static void graphwindow_destroy( GtkObject *object ) { Graphwindow *graphwindow; g_return_if_fail( object != NULL ); g_return_if_fail( IS_GRAPHWINDOW( object ) ); graphwindow = GRAPHWINDOW( object ); #ifdef DEBUG printf( "graphwindow_destroy: %p\n", graphwindow ); #endif /*DEBUG*/ /* My instance destroy stuff. */ IM_FREE( graphwindow->dot ); IM_FREEF( g_source_remove, graphwindow->layout_timeout ); UNREF( graphwindow->imagemodel ); IM_FREEF( agclose, graphwindow->graph ); IM_FREEF( gvFreeContext, graphwindow->gvc ); FREESID( graphwindow->workspace_changed_sid, FLOATWINDOW( graphwindow )->model ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void graphwindow_class_init( GraphwindowClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = graphwindow_destroy; /* Create signals. */ /* Init methods. */ } static void graphwindow_init( Graphwindow *graphwindow ) { #ifdef DEBUG printf( "graphwindow_init: %p\n", graphwindow ); #endif /*DEBUG*/ graphwindow->dot = NULL; graphwindow->layout_timeout = 0; graphwindow->gvc = gvContext(); } GType graphwindow_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( GraphwindowClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) graphwindow_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Graphwindow ), 32, /* n_preallocs */ (GInstanceInitFunc) graphwindow_init, }; type = g_type_register_static( TYPE_FLOATWINDOW, "Graphwindow", &info, 0 ); } return( type ); } static void graphwindow_refresh_title( Graphwindow *graphwindow ) { Workspace *ws = WORKSPACE( FLOATWINDOW( graphwindow )->model ); VipsBuf buf; char txt[512]; #ifdef DEBUG printf( "graphwindow_refresh_title\n" ); #endif /*DEBUG*/ vips_buf_init_static( &buf, txt, 512 ); if( ws->sym ) symbol_qualified_name( ws->sym, &buf ); iwindow_set_title( IWINDOW( graphwindow ), "%s", vips_buf_all( &buf ) ); } static gboolean graphwindow_build_graph( Graphwindow *graphwindow ) { char tname[FILENAME_MAX]; iOpenFile *of; if( !temp_name( tname, "dot" ) || !(of = ifile_open_write( "%s", tname )) ) return( FALSE ); if( !ifile_write( of, "%s", graphwindow->dot ) ) { ifile_close( of ); unlinkf( "%s", tname ); return( FALSE ); } ifile_close( of ); if( !(of = ifile_open_read( "%s", tname )) ) { unlinkf( "%s", tname ); return( FALSE ); } IM_FREEF( agclose, graphwindow->graph ); graphwindow->graph = agread( of->fp, NULL ); ifile_close( of ); unlinkf( "%s", tname ); return( TRUE ); } static gboolean graphwindow_update_image( Graphwindow *graphwindow ) { char tname[FILENAME_MAX]; iOpenFile *of; Imageinfo *ii; if( !temp_name( tname, "png" ) || !(of = ifile_open_write( "%s", tname )) ) return( FALSE ); gvRender( graphwindow->gvc, graphwindow->graph, "png:cairo", of->fp ); ifile_close( of ); if( !(ii = imageinfo_new_input( main_imageinfogroup, GTK_WIDGET( graphwindow ), NULL, tname )) ) { unlinkf( "%s", tname ); return( FALSE ); } conversion_set_image( graphwindow->imagemodel->conv, ii ); MANAGED_UNREF( ii ); /* We can unlink now: the png will have been converted to vips * format. */ unlinkf( "%s", tname ); return( TRUE ); } static gboolean graphwindow_layout( Graphwindow *graphwindow ) { if( !graphwindow_build_graph( graphwindow ) ) return( FALSE ); gvLayout( graphwindow->gvc, graphwindow->graph, "dot" ); if( !graphwindow_update_image( graphwindow ) ) return( FALSE ); return( TRUE ); } static gboolean graphwindow_layout_cb( Graphwindow *graphwindow ) { Workspace *ws = WORKSPACE( FLOATWINDOW( graphwindow )->model ); char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graphwindow->layout_timeout = 0; graph_write_dot( ws, &buf ); if( !graphwindow->dot || strcmp( vips_buf_all( &buf ), graphwindow->dot ) != 0 ) { IM_FREE( graphwindow->dot ); graphwindow->dot = im_strdup( NULL, vips_buf_all( &buf ) ); #ifdef DEBUG printf( "graphwindow_changed_cb:\n%s\n", graphwindow->dot ); #endif /*DEBUG*/ if( !graphwindow_layout( graphwindow ) ) iwindow_alert( GTK_WIDGET( graphwindow ), GTK_MESSAGE_ERROR ); } /* Clear the timeout. */ return( FALSE ); } static void graphwindow_layout_queue( Graphwindow *graphwindow ) { IM_FREEF( g_source_remove, graphwindow->layout_timeout ); graphwindow->layout_timeout = g_timeout_add( 200, (GSourceFunc) graphwindow_layout_cb, graphwindow ); } /* The model has changed. */ static void graphwindow_changed_cb( Workspace *ws, Graphwindow *graphwindow ) { #ifdef DEBUG printf( "graphwindow_changed_cb: %p\n", graphwindow ); #endif /*DEBUG*/ graphwindow_refresh_title( graphwindow ); graphwindow_layout_queue( graphwindow ); } static const char *graphwindow_menubar_ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " ""; static void graphwindow_build( Graphwindow *graphwindow, GtkWidget *vbox, Workspace *ws ) { iWindow *iwnd = IWINDOW( graphwindow ); GError *error; GtkWidget *mbar; GtkWidget *frame; /* Make our model. */ graphwindow->imagemodel = imagemodel_new( NULL ); g_object_ref( G_OBJECT( graphwindow->imagemodel ) ); iobject_sink( IOBJECT( graphwindow->imagemodel ) ); graphwindow->workspace_changed_sid = g_signal_connect( G_OBJECT( ws ), "changed", G_CALLBACK( graphwindow_changed_cb ), graphwindow ); /* Make main menu bar */ error = NULL; if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager, graphwindow_menubar_ui_description, -1, &error ) ) { g_message( "building menus failed: %s", error->message ); g_error_free( error ); exit( EXIT_FAILURE ); } mbar = gtk_ui_manager_get_widget( iwnd->ui_manager, "/GraphwindowMenubar" ); gtk_box_pack_start( GTK_BOX( vbox ), mbar, FALSE, FALSE, 0 ); gtk_widget_show( mbar ); /* This will set to NULL if we don't have infobar support. */ if( (iwnd->infobar = infobar_new()) ) gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( iwnd->infobar ), FALSE, FALSE, 0 ); /* Graph area. */ frame = gtk_frame_new( NULL ); gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_OUT ); gtk_widget_show( frame ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 ); graphwindow->ip = imagepresent_new( graphwindow->imagemodel ); gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( graphwindow->ip ) ); gtk_widget_show( GTK_WIDGET( graphwindow->ip ) ); } static void graphwindow_link( Graphwindow *graphwindow, Workspace *ws, GtkWidget *parent ) { iwindow_set_build( IWINDOW( graphwindow ), (iWindowBuildFn) graphwindow_build, ws, NULL, NULL ); iwindow_set_parent( IWINDOW( graphwindow ), parent ); floatwindow_link( FLOATWINDOW( graphwindow ), MODEL( ws ) ); iwindow_set_size_prefs( IWINDOW( graphwindow ), "GRAPH_WINDOW_WIDTH", "GRAPH_WINDOW_HEIGHT" ); iwindow_build( IWINDOW( graphwindow ) ); /* Initial "changed" on the model to get all views to init. */ iobject_changed( IOBJECT( ws ) ); } Graphwindow * graphwindow_new( Workspace *ws, GtkWidget *parent ) { Graphwindow *graphwindow = gtk_type_new( TYPE_GRAPHWINDOW ); graphwindow_link( graphwindow, ws, parent ); return( graphwindow ); } #endif /*HAVE_LIBGVC*/ ================================================ FILE: src/graphwindow.h ================================================ /* display workspaces with graphviz */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_GRAPHWINDOW (graphwindow_get_type()) #define GRAPHWINDOW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_GRAPHWINDOW, Graphwindow )) #define GRAPHWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_GRAPHWINDOW, GraphwindowClass )) #define IS_GRAPHWINDOW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_GRAPHWINDOW )) #define IS_GRAPHWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_GRAPHWINDOW )) struct _Graphwindow { Floatwindow parent_class; /* The last dot graph we generated. */ char *dot; /* Regenerate the graph on a timeout to avoid regen on many small * changes. */ guint layout_timeout; /* The imagedisplay we make. */ Imagemodel *imagemodel; Imagepresent *ip; /* Watch the ws with this. */ guint workspace_changed_sid; #ifdef HAVE_LIBGVC GVC_t *gvc; graph_t *graph; #endif /*HAVE_LIBGVC*/ }; typedef struct _GraphwindowClass { FloatwindowClass parent_class; /* My methods. */ } GraphwindowClass; void graph_write( Workspace *ws ); GtkType graphwindow_get_type( void ); Graphwindow *graphwindow_new( Workspace *ws, GtkWidget *parent ); ================================================ FILE: src/group.c ================================================ /* an input group ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ValueClass *parent_class = NULL; static gboolean group_save_list( PElement *list, char *filename ); /* Exported, since main.c uses this to save 'main' to a file. @filename is * incremented. */ gboolean group_save_item( PElement *item, char *filename ) { gboolean result; Imageinfo *ii; char buf[FILENAME_MAX]; /* We don't want $VAR etc. in the filename we pass down to the file * ops. */ im_strncpy( buf, filename, FILENAME_MAX ); path_expand( buf ); if( !heap_is_instanceof( CLASS_GROUP, item, &result ) ) return( FALSE ); if( result ) { PElement value; if( !class_get_member( item, MEMBER_VALUE, NULL, &value ) || !group_save_list( &value, filename ) ) return( FALSE ); } if( !heap_is_instanceof( CLASS_IMAGE, item, &result ) ) return( FALSE ); if( result ) { PElement value; filesel_add_mode( buf ); if( !class_get_member( item, MEMBER_VALUE, NULL, &value ) || !heap_get_image( &value, &ii ) || !imageinfo_write( ii, buf ) ) return( FALSE ); increment_filename( filename ); } if( !heap_is_instanceof( CLASS_MATRIX, item, &result ) ) return( FALSE ); if( result ) { DOUBLEMASK *dmask; if( !(dmask = matrix_ip_to_dmask( item )) ) return( FALSE ); if( im_write_dmask_name( dmask, buf ) ) { error_vips_all(); IM_FREEF( im_free_dmask, dmask ); return( FALSE ); } IM_FREEF( im_free_dmask, dmask ); increment_filename( filename ); } if( PEISIMAGE( item ) ) { filesel_add_mode( buf ); if( !heap_get_image( item, &ii ) || !imageinfo_write( ii, buf ) ) return( FALSE ); increment_filename( filename ); } if( PEISLIST( item ) ) { if( !group_save_list( item, filename ) ) return( FALSE ); } return( TRUE ); } static gboolean group_save_list( PElement *list, char *filename ) { int i; int length; if( (length = heap_list_length( list )) < 0 ) return( FALSE ); for( i = 0; i < length; i++ ) { PElement item; if( !heap_list_index( list, i, &item ) || !group_save_item( &item, filename ) ) return( FALSE ); } return( TRUE ); } static gboolean group_graphic_save( Classmodel *classmodel, GtkWidget *parent, const char *filename ) { Group *group = GROUP( classmodel ); Row *row = HEAPMODEL( group )->row; PElement *root = &row->expr->root; char buf[FILENAME_MAX]; /* We are going to increment the filename ... make sure there's some * space at the end of the string. */ im_strncpy( buf, filename, FILENAME_MAX - 5 ); if( !group_save_item( root, buf ) ) return( FALSE ); return( TRUE ); } static void group_class_init( GroupClass *class ) { ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ classmodel_class->graphic_save = group_graphic_save; model_register_loadable( MODEL_CLASS( class ) ); } static void group_init( Group *group ) { iobject_set( IOBJECT( group ), CLASS_GROUP, NULL ); } GType group_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( GroupClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) group_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Group ), 32, /* n_preallocs */ (GInstanceInitFunc) group_init, }; type = g_type_register_static( TYPE_VALUE, "Group", &info, 0 ); } return( type ); } ================================================ FILE: src/group.h ================================================ /* a group in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_GROUP (group_get_type()) #define GROUP( obj ) (GTK_CHECK_CAST( (obj), TYPE_GROUP, Group )) #define GROUP_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_GROUP, GroupClass )) #define IS_GROUP( obj ) (GTK_CHECK_TYPE( (obj), TYPE_GROUP )) #define IS_GROUP_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_GROUP )) typedef struct _Group { Value parent_object; } Group; typedef struct _GroupClass { ValueClass parent_class; /* My methods. */ } GroupClass; GType group_get_type( void ); gboolean group_save_item( PElement *item, char *filename ); ================================================ FILE: src/gtkutil.c ================================================ /* gtkutil 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* All our tooltips. */ static GtkTooltips *our_tooltips = NULL; /* Set two adjustments together. */ void adjustments_set_value( GtkAdjustment *hadj, GtkAdjustment *vadj, float hval, float vval ) { gboolean hchanged = FALSE; gboolean vchanged = FALSE; if( hval != hadj->value ) { hadj->value = hval; hchanged = TRUE; } if( vval != vadj->value ) { vadj->value = vval; vchanged = TRUE; } #ifdef DEBUG if( hchanged ) printf( "adjustments_set_value: hadj = %g\n", hval ); if( vchanged ) printf( "adjustments_set_value: vadj = %g\n", vval ); #endif /*DEBUG*/ if( hchanged ) gtk_adjustment_value_changed( hadj ); if( vchanged ) gtk_adjustment_value_changed( vadj ); } void * object_destroy( void *obj ) { gtk_object_destroy( GTK_OBJECT( obj ) ); return( NULL ); } /* Like g_free, but return NULL for list maps. */ void * null_g_free( void *obj ) { g_free( obj ); return( NULL ); } /* Set visible/not. */ void widget_visible( GtkWidget *widget, gboolean visible ) { if( widget && visible ) gtk_widget_show( widget ); else if( widget && !visible ) gtk_widget_hide( widget ); } /* Make a button widget. */ GtkWidget * build_button( const char *stock_id, GtkSignalFunc cb, gpointer user ) { GtkWidget *but; but = gtk_button_new_from_stock( stock_id ); GTK_WIDGET_SET_FLAGS( but, GTK_CAN_DEFAULT ); gtk_signal_connect( GTK_OBJECT( but ), "clicked", cb, user ); return( but ); } /* Calculate the bounding box for a string rendered with a widget's default * font. Set geo to a rect with 0,0 positioned on the left-hand baseline. */ void get_geo( GtkWidget *widget, const char *text, Rect *geo ) { PangoLayout *layout; int width, height; layout = gtk_widget_create_pango_layout( widget, text ); pango_layout_get_pixel_size( layout, &width, &height ); g_object_unref( layout ); /* FIXME ... we left/top to 0 for now. */ geo->width = width; geo->height = height; geo->left = 0; geo->top = 0; } /* Set a widget to a fixed size ... width in characters. */ void set_fixed( GtkWidget *widget, int nchars ) { Rect geo; get_geo( widget, "8", &geo ); gtk_widget_set_size_request( widget, geo.width * nchars, geo.height ); } /* Build a GtkEntry, with a widget width specified in characters. */ GtkWidget * build_entry( int nchars ) { GtkWidget *entry; entry = gtk_entry_new(); gtk_entry_set_width_chars( GTK_ENTRY( entry ), nchars ); return( entry ); } /* Build a new menu. */ GtkWidget * menu_build( const char *name ) { GtkWidget *menu; menu = gtk_menu_new(); gtk_menu_set_title( GTK_MENU( menu ), name ); return( menu ); } /* Add a menu item. */ GtkWidget * menu_add_but( GtkWidget *menu, const char *stock_id, GtkSignalFunc cb, void *user ) { GtkWidget *but; /* We don't provide an accel group for popup menus. */ but = gtk_image_menu_item_new_from_stock( stock_id, NULL ); gtk_menu_shell_append( GTK_MENU_SHELL( menu ), but ); gtk_widget_show( but ); gtk_signal_connect( GTK_OBJECT( but ), "activate", cb, user ); return( but ); } /* Add a toggle item. */ GtkWidget * menu_add_tog( GtkWidget *menu, const char *name, GtkSignalFunc cb, void *user ) { GtkWidget *tog; tog = gtk_check_menu_item_new_with_mnemonic( name ); gtk_menu_shell_append( GTK_MENU_SHELL( menu ), tog ); gtk_widget_show( tog ); gtk_signal_connect( GTK_OBJECT( tog ), "toggled", cb, user ); return( tog ); } /* Add a separator. */ GtkWidget * menu_add_sep( GtkWidget *menu ) { GtkWidget *sep; sep = gtk_menu_item_new(); gtk_widget_set_sensitive( GTK_WIDGET( sep ), FALSE ); gtk_menu_shell_append( GTK_MENU_SHELL( menu ), sep ); gtk_widget_show( sep ); return( sep ); } /* Add a pullright. */ GtkWidget * menu_add_pullright( GtkWidget *menu, const char *stock_id ) { GtkWidget *pullright; GtkWidget *subpane; subpane = gtk_menu_new(); pullright = gtk_image_menu_item_new_from_stock( stock_id, NULL ); gtk_menu_item_set_submenu( GTK_MENU_ITEM( pullright ), subpane ); gtk_menu_shell_append( GTK_MENU_SHELL( menu ), pullright ); gtk_widget_show( pullright ); return( subpane ); } /* Four quarks: each menu item has a quark linking back to the main pane, * plus a quark for the user signal. The main pane has a quark linking to the * widget the menu was popped from, and that has the userdata for this context. * One more quark holds the popup in a host. */ static GQuark quark_main = 0; static GQuark quark_host = 0; static GQuark quark_data = 0; static GQuark quark_popup = 0; /* Build a new popup menu. */ GtkWidget * popup_build( const char *name ) { /* Build our quarks. */ if( !quark_main ) { quark_main = g_quark_from_static_string( "quark_main" ); quark_host = g_quark_from_static_string( "quark_host" ); quark_data = g_quark_from_static_string( "quark_data" ); quark_popup = g_quark_from_static_string( "quark_popup" ); } return( menu_build( name ) ); } /* Activate function for a popup menu item. */ static void popup_activate_cb( GtkWidget *item, PopupFunc cb ) { GtkWidget *qmain = gtk_object_get_data_by_id( GTK_OBJECT( item ), quark_main ); GtkWidget *qhost = gtk_object_get_data_by_id( GTK_OBJECT( qmain ), quark_host ); void *qdata = gtk_object_get_data_by_id( GTK_OBJECT( qhost ), quark_data ); (*cb)( item, qhost, qdata ); } /* Add a menu item to a popup. */ GtkWidget * popup_add_but( GtkWidget *popup, const char *name, PopupFunc cb ) { GtkWidget *but = menu_add_but( popup, name, GTK_SIGNAL_FUNC( popup_activate_cb ), (void *) cb ); gtk_object_set_data_by_id( GTK_OBJECT( but ), quark_main, popup ); return( but ); } /* Add a toggle item to a popup. */ GtkWidget * popup_add_tog( GtkWidget *popup, const char *name, PopupFunc cb ) { GtkWidget *tog = menu_add_tog( popup, name, GTK_SIGNAL_FUNC( popup_activate_cb ), (void *) cb ); gtk_object_set_data_by_id( GTK_OBJECT( tog ), quark_main, popup ); return( tog ); } /* Add a pullright item to a popup. Return the empty sub-pane. */ GtkWidget * popup_add_pullright( GtkWidget *popup, const char *name ) { GtkWidget *pullright = menu_add_pullright( popup, name ); gtk_object_set_data_by_id( GTK_OBJECT( pullright ), quark_main, popup ); return( pullright ); } /* Show the popup. */ void popup_show( GtkWidget *host, GdkEvent *ev ) { GtkWidget *popup = gtk_object_get_data_by_id( GTK_OBJECT( host ), quark_popup ); gtk_object_set_data_by_id( GTK_OBJECT( popup ), quark_host, host ); gtk_menu_popup( GTK_MENU( popup ), NULL, NULL, (GtkMenuPositionFunc) NULL, NULL, 3, ev->button.time ); } /* Event handler for popupshow. */ static gboolean popup_handle_event( GtkWidget *host, GdkEvent *ev, gpointer dummy ) { gboolean handled = FALSE; if( ev->type == GDK_BUTTON_PRESS && ev->button.button == 3 ) { popup_show( host, ev ); handled = TRUE; } else if( ev->type == GDK_KEY_PRESS && ev->key.keyval == GDK_F10 && ev->key.state & GDK_SHIFT_MASK ) { popup_show( host, ev ); handled = TRUE; } return( handled ); } /* Link a host to a popup. */ void popup_link( GtkWidget *host, GtkWidget *popup, void *data ) { gtk_object_set_data_by_id( GTK_OBJECT( host ), quark_popup, popup ); gtk_object_set_data_by_id( GTK_OBJECT( host ), quark_data, data ); } /* Add a callback to show a popup. */ guint popup_attach( GtkWidget *host, GtkWidget *popup, void *data ) { guint sid; popup_link( host, popup, data ); /* We can't just use gtk_menu_attach_to_widget(), since that can only * attach a menu to a single widget. We want to be able to attach a * single menu to meny widgets. */ sid = gtk_signal_connect( GTK_OBJECT( host ), "event", GTK_SIGNAL_FUNC( popup_handle_event ), NULL ); return( sid ); } void popup_detach( GtkWidget *host, guint sid ) { gtk_signal_disconnect( GTK_OBJECT( host ), sid ); } static void set_tooltip_events( GtkWidget *wid ) { gtk_widget_add_events( wid, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); } /* Set the tooltip on a widget. */ void set_tooltip( GtkWidget *wid, const char *fmt, ... ) { va_list ap; char *txt; if( !wid ) return; if( !fmt ) fmt = ""; va_start( ap, fmt ); txt = g_strdup_vprintf( fmt, ap ); va_end( ap ); if( !our_tooltips ) our_tooltips = gtk_tooltips_new(); gtk_tooltips_set_tip( our_tooltips, wid, txt, NULL ); if( !GTK_WIDGET_REALIZED( wid ) ) gtk_signal_connect( GTK_OBJECT( wid ), "realize", GTK_SIGNAL_FUNC( set_tooltip_events ), NULL ); else set_tooltip_events( wid ); g_free( txt ); } /* Track tooltips we generate with one of these. */ typedef struct _TooltipGenerate { GtkWidget *widget; TooltipGenerateFn generate; void *a; void *b; VipsBuf buf; char txt[256]; } TooltipGenerate; static void tooltip_generate_free( GtkWidget *widget, TooltipGenerate *gen ) { gen->widget = NULL; gen->generate = NULL; gen->a = NULL; gen->b = NULL; IM_FREE( gen ); } static gboolean tooltip_generate_rebuild( GtkWidget *widget, GdkEventCrossing *event, TooltipGenerate *gen ) { gboolean handled = FALSE; if( gen->widget ) { vips_buf_rewind( &gen->buf ); gen->generate( widget, &gen->buf, gen->a, gen->b ); set_tooltip( gen->widget, "%s", vips_buf_all( &gen->buf ) ); } return( handled ); } static void tooltip_generate_attach( GtkWidget *widget, TooltipGenerate *gen ) { /* Must have enter/leave. */ gtk_widget_add_events( widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); /* On enter, regenerate the tooltip. */ g_signal_connect( widget, "enter_notify_event", G_CALLBACK( tooltip_generate_rebuild ), gen ); } /* Set a callback to be used to generate the tooltip. */ void set_tooltip_generate( GtkWidget *widget, TooltipGenerateFn generate, void *a, void *b ) { TooltipGenerate *gen; if( !(gen = INEW( NULL, TooltipGenerate )) ) return; gen->widget = widget; gen->generate = generate; gen->a = a; gen->b = b; vips_buf_init_static( &gen->buf, gen->txt, 256 ); g_signal_connect( widget, "destroy", G_CALLBACK( tooltip_generate_free ), gen ); if( !GTK_WIDGET_REALIZED( widget ) ) g_signal_connect( widget, "realize", G_CALLBACK( tooltip_generate_attach ), gen ); else tooltip_generate_attach( widget, gen ); } /* Junk all tooltips, helps trim valgrind noise. */ void junk_tooltips( void ) { if( our_tooltips ) g_object_ref_sink( GTK_OBJECT( our_tooltips ) ); } /* Set a GtkEditable. */ void set_gentryv( GtkWidget *edit, const char *fmt, va_list ap ) { char buf[1000]; gint position; int i; int len; if( !edit ) return; if( !fmt ) fmt = ""; (void) im_vsnprintf( buf, 1000, fmt, ap ); /* Filter out /n and /t ... they confuse gtkentry terribly */ len = strlen( buf ); for( i = 0; i < len; i++ ) if( buf[i] == '\n' || buf[i] == '\t' ) buf[i] = ' '; gtk_editable_delete_text( GTK_EDITABLE( edit ), 0, -1 ); position = 0; gtk_editable_insert_text( GTK_EDITABLE( edit ), buf, strlen( buf ), &position ); } /* Set a GtkEditable. */ void set_gentry( GtkWidget *edit, const char *fmt, ... ) { va_list ap; va_start( ap, fmt ); set_gentryv( edit, fmt, ap ); va_end( ap ); } void set_glabel( GtkWidget *label, const char *fmt, ... ) { va_list ap; char buf[1000]; va_start( ap, fmt ); (void) im_vsnprintf( buf, 1000, fmt, ap ); va_end( ap ); gtk_label_set_text( GTK_LABEL( label ), buf ); } /* Like set_glabel(), but don't display multi-line strings (just display the * first line). */ void set_glabel1( GtkWidget *label, const char *fmt, ... ) { va_list ap; char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); va_start( ap, fmt ); vips_buf_vappendf( &buf, fmt, ap ); va_end( ap ); gtk_label_set_text( GTK_LABEL( label ), vips_buf_firstline( &buf ) ); } /* Like set_glabel, but do it caption-style. */ void set_gcaption( GtkWidget *label, const char *fmt, ... ) { va_list ap; char buf1[1000]; char buf2[1000]; va_start( ap, fmt ); (void) im_vsnprintf( buf1, 1000, fmt, ap ); va_end( ap ); escape_markup( buf1, buf2, 1000 ); (void) im_snprintf( buf1, 1000, "%s", buf2 ); gtk_label_set_markup( GTK_LABEL( label ), buf1 ); } gboolean get_geditable_name( GtkWidget *text, char *out, int sz ) { char *name; char *tname; name = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 ); tname = trim_nonalpha( name ); if( !tname ) { IM_FREEF( g_free, name ); error_top( _( "Bad identifier." ) ); error_sub( _( "Enter an identifier. Identifiers start with " "a letter, and then contain only letters, numbers, " "apostrophy and underscore." ) ); return( FALSE ); } im_strncpy( out, tname, sz ); g_free( name ); return( TRUE ); } gboolean get_geditable_string( GtkWidget *text, char *out, int sz ) { char *str; str = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 ); im_strncpy( out, str, sz ); g_free( str ); return( TRUE ); } gboolean get_geditable_filename( GtkWidget *text, char *out, int sz ) { char *filename; char *tfilename; filename = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 ); tfilename = trim_white( filename ); if( !is_valid_filename( tfilename ) ) { g_free( filename ); return( FALSE ); } im_strncpy( out, tfilename, sz ); g_free( filename ); return( TRUE ); } /* Get a geditable as a double. */ gboolean get_geditable_double( GtkWidget *text, double *out ) { char *txt; char *end; double t; txt = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 ); t = strtod( txt, &end ); if( end == txt ) { error_top( _( "Bad floating point number." ) ); error_sub( _( "\"%s\" is not a floating point number." ), txt ); g_free( txt ); return( FALSE ); } if( strspn( end, WHITESPACE ) != strlen( end ) ) { error_top( _( "Bad floating point number." ) ); error_sub( _( "Extra characters \"%s\" after number." ), end ); g_free( txt ); return( FALSE ); } g_free( txt ); *out = t; return( TRUE ); } /* Get as int. */ gboolean get_geditable_int( GtkWidget *text, int *n ) { int i; char *txt; /* Parse values. */ txt = gtk_editable_get_chars( GTK_EDITABLE( text ), 0, -1 ); if( sscanf( txt, "%i", &i ) != 1 ) { error_top( _( "Bad integer." ) ); error_sub( _( "\"%s\" is not an integer." ), txt ); g_free( txt ); return( FALSE ); } g_free( txt ); *n = i; return( TRUE ); } /* Get as unsigned int. */ gboolean get_geditable_uint( GtkWidget *text, int *n ) { int i; if( !get_geditable_int( text, &i ) || i < 0 ) { error_top( _( "Bad unsigned integer." ) ); return( FALSE ); } *n = i; return( TRUE ); } /* Get as positive int. */ gboolean get_geditable_pint( GtkWidget *text, int *n ) { int i; if( !get_geditable_int( text, &i ) || i <= 0 ) { error_top( _( "Bad positive integer." ) ); return( FALSE ); } *n = i; return( TRUE ); } /* Indent widget, label above. */ GtkWidget * build_glabelframe2( GtkWidget *widget, const char *name ) { GtkWidget *lab; GtkWidget *vb; GtkWidget *hb; GtkWidget *inv; char buf[1000]; hb = gtk_hbox_new( FALSE, 2 ); inv = gtk_label_new( "" ); gtk_box_pack_start( GTK_BOX( hb ), inv, FALSE, FALSE, 15 ); gtk_box_pack_start( GTK_BOX( hb ), widget, TRUE, TRUE, 0 ); vb = gtk_vbox_new( FALSE, 2 ); im_snprintf( buf, 1000, _( "%s:" ), name ); lab = gtk_label_new( buf ); gtk_misc_set_alignment( GTK_MISC( lab ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( vb ), lab, FALSE, FALSE, 0 ); gtk_box_pack_start( GTK_BOX( vb ), hb, TRUE, TRUE, 0 ); return( vb ); } /* Make a text field + label. Indent the text on a new line. */ GtkWidget * build_glabeltext3( GtkWidget *box, const char *label ) { GtkWidget *txt; GtkWidget *vb; txt = gtk_entry_new(); vb = build_glabelframe2( txt, label ); gtk_box_pack_start( GTK_BOX( box ), vb, FALSE, FALSE, 0 ); return( txt ); } /* Make text field plus label .. use a sizegroup for alignment. */ GtkWidget * build_glabeltext4( GtkWidget *box, GtkSizeGroup *group, const char *text ) { GtkWidget *hbox; GtkWidget *label; GtkWidget *entry; char buf[256]; hbox = gtk_hbox_new( FALSE, 12 ); im_snprintf( buf, 256, _( "%s:" ), text ); label = gtk_label_new( buf ); gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); if( group ) gtk_size_group_add_widget( group, label ); gtk_box_pack_start( GTK_BOX( hbox ), label, FALSE, FALSE, 0 ); entry = gtk_entry_new(); gtk_box_pack_start( GTK_BOX( hbox ), entry, TRUE, TRUE, 0 ); gtk_box_pack_start( GTK_BOX( box ), hbox, FALSE, FALSE, 0 ); gtk_widget_show_all( hbox ); return( entry ); } /* Make a labeled toggle. */ GtkWidget * build_gtoggle( GtkWidget *box, const char *caption ) { GtkWidget *hb; GtkWidget *inv; GtkWidget *toggle; /* Indent left a bit. */ inv = gtk_label_new( "" ); hb = gtk_hbox_new( FALSE, 0 ); gtk_box_pack_start( GTK_BOX( hb ), inv, FALSE, FALSE, 2 ); toggle = gtk_check_button_new_with_label( caption ); gtk_container_set_border_width( GTK_CONTAINER( toggle ), 4 ); gtk_box_pack_start( GTK_BOX( hb ), toggle, TRUE, TRUE, 0 ); gtk_box_pack_start( GTK_BOX( box ), hb, FALSE, FALSE, 0 ); return( toggle ); } /* Make a label plus option menu. */ GtkWidget * build_goption( GtkWidget *box, GtkSizeGroup *group, const char *name, const char *item_names[], int nitem, GtkSignalFunc fn, void *value ) { GtkWidget *hb; GtkWidget *label; GtkWidget *om; int i; char buf[1000]; hb = gtk_hbox_new( FALSE, 12 ); im_snprintf( buf, 1000, _( "%s:" ), name ); label = gtk_label_new( buf ); if( group ) gtk_size_group_add_widget( group, label ); gtk_box_pack_start( GTK_BOX( hb ), label, FALSE, TRUE, 0 ); om = gtk_combo_box_new_text(); gtk_box_pack_start( GTK_BOX( hb ), om, FALSE, TRUE, 0 ); set_tooltip( om, _( "Left-click to change value" ) ); for( i = 0; i < nitem; i++ ) gtk_combo_box_append_text( GTK_COMBO_BOX( om ), _( item_names[i] ) ); if( fn ) gtk_signal_connect( GTK_OBJECT( om ), "changed", fn, value ); gtk_box_pack_start( GTK_BOX( box ), hb, FALSE, TRUE, 0 ); gtk_widget_show_all( hb ); return( om ); } /* Register a widget as a filename drag receiver. */ typedef struct { GtkWidget *widget; FiledropFunc fn; void *client; } FiledropInfo; static gboolean filedrop_trigger( FiledropInfo *fdi, const char *path ) { char buf[FILENAME_MAX]; gboolean result; im_strncpy( buf, path, FILENAME_MAX ); path_compact( buf ); result = fdi->fn( fdi->client, buf ); return( result ); } static void filedrop_drag_data_received( GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, FiledropInfo *fdi ) { gchar *sPath = NULL; gchar *pFrom, *pTo; gboolean result; pFrom = strstr( (char *) data->data, "file:" ); while( pFrom ) { #if !GLIB_CHECK_VERSION (2,0,0) pFrom += 5; /* remove 'file:' */ #else GError *error = NULL; #endif pTo = pFrom; while( *pTo != 0 && *pTo != 0xd && *pTo != 0xa ) pTo += 1; sPath = g_strndup( pFrom, pTo - pFrom ); #if !GLIB_CHECK_VERSION (2,0,0) result = filedrop_trigger( fdi, sPath ); #else /* format changed with Gtk+1.3, use conversion */ pFrom = g_filename_from_uri( sPath, NULL, &error ); result = filedrop_trigger( fdi, pFrom ); g_free( pFrom ); #endif g_free( sPath ); if( !result ) iwindow_alert( fdi->widget, GTK_MESSAGE_ERROR ); pFrom = strstr( pTo, "file:" ); } gtk_drag_finish( context, TRUE, FALSE, time ); } /* HB: file dnd stuff lent by The Gimp via Dia, not fully understood * but working ... */ enum { TARGET_URI_LIST, TARGET_TEXT_PLAIN }; static void filedrop_destroy( GtkWidget *widget, FiledropInfo *fdi ) { im_free( fdi ); } void filedrop_register( GtkWidget *widget, FiledropFunc fn, void *client ) { static GtkTargetEntry target_table[] = { { "text/uri-list", 0, TARGET_URI_LIST }, { "text/plain", 0, TARGET_TEXT_PLAIN } }; FiledropInfo *fdi = INEW( NULL, FiledropInfo ); fdi->widget = widget; fdi->fn = fn; fdi->client = client; gtk_signal_connect( GTK_OBJECT( widget ), "destroy", GTK_SIGNAL_FUNC( filedrop_destroy ), fdi ); gtk_drag_dest_set( GTK_WIDGET( widget ), GTK_DEST_DEFAULT_ALL, target_table, IM_NUMBER( target_table ), GDK_ACTION_COPY | /* That's all you need to get draggable URIs in GNOME and * win32, but KDE needs these other flags too, apparently. */ GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK ); gtk_signal_connect( GTK_OBJECT( widget ), "drag_data_received", GTK_SIGNAL_FUNC( filedrop_drag_data_received ), fdi ); } /* Add symbol drag to the target list. */ void set_symbol_drag_type( GtkWidget *widget ) { static const GtkTargetEntry targets[] = { { "text/symbol", 0, TARGET_SYMBOL } }; GtkTargetList *target_list; if( !GTK_WIDGET_REALIZED( widget ) ) return; /* We can't always set the dest types, since we're probably already a * filedrop. Just add to the target list. */ if( (target_list = gtk_drag_dest_get_target_list( widget )) ) gtk_target_list_add_table( target_list, targets, IM_NUMBER( targets ) ); else gtk_drag_dest_set( widget, GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, targets, IM_NUMBER( targets ), GDK_ACTION_COPY ); gtk_drag_source_set( widget, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, targets, IM_NUMBER( targets ), GDK_ACTION_COPY | GDK_ACTION_MOVE ); } typedef struct _Listen { GObject *gobject; /* This object */ GObject *source; /* Listens for signals from this */ GObject **zap; /* NULL this on destroy */ const char *name; /* Signal name */ GCallback gcallback; /* Call this handler */ guint name_sid; guint gobject_destroy_sid; guint source_destroy_sid; } Listen; static void listen_gobject_destroy_cb( GObject *gobject, Listen *listen ) { /* gobject has gone ... source should no longer send us signals. */ FREESID( listen->name_sid, listen->source ); FREESID( listen->source_destroy_sid, listen->source ); g_free( listen ); } static void listen_source_destroy_cb( GObject *gobject, Listen *listen ) { /* Source has gone, these signals have been destroyed. */ listen->name_sid = 0; listen->source_destroy_sid = 0; /* Link broken, no need to auto-free us on gobject destroy. */ FREESID( listen->gobject_destroy_sid, listen->gobject ); /* Zap gobject member pointer to source. */ if( listen->zap ) { g_assert( !*(listen->zap) || *(listen->zap) == listen->source ); *(listen->zap) = NULL; } g_free( listen ); } void listen_add( GObject *gobject, GObject **zap, const char *name, GCallback gcallback ) { Listen *listen = g_new( Listen, 1 ); listen->gobject = gobject; listen->source = *zap; listen->zap = zap; listen->name = name; listen->gcallback = gcallback; listen->name_sid = g_signal_connect( listen->source, listen->name, listen->gcallback, listen->gobject ); listen->source_destroy_sid = g_signal_connect( listen->source, "destroy", G_CALLBACK( listen_source_destroy_cb ), listen ); listen->gobject_destroy_sid = g_signal_connect( gobject, "destroy", G_CALLBACK( listen_gobject_destroy_cb ), listen ); } void widget_update_pointer( GtkWidget *widget, GdkEvent *ev ) { if( ev->type == GDK_MOTION_NOTIFY && ev->motion.is_hint ) { GdkDisplay *display = gtk_widget_get_display( widget ); GdkScreen *screen; int x_root, y_root; gdk_display_get_pointer( display, &screen, &x_root, &y_root, NULL ); ev->motion.x_root = x_root; ev->motion.y_root = y_root; } } void * gobject_print( GObject *gobject ) { printf( "%s (%p)\n", G_OBJECT_TYPE_NAME( gobject ), gobject ); return( NULL ); } /* Get the default DPI. */ int get_dpi( void ) { GdkScreen *screen = gdk_screen_get_default(); if( screen ) { int width_pixels = gdk_screen_get_width( screen ); int width_mm = gdk_screen_get_width_mm( screen ); return( width_pixels / (width_mm / 25.4) ); } else return( 72 ); } GtkWidget * image_new_from_file( const char *name ) { GtkWidget *image; char *file; if( (file = path_find_file( name )) ) { image = (GtkWidget *) callv_string_filename( (callv_string_fn) gtk_image_new_from_file, file, NULL, NULL, NULL ); im_free( file ); } else /* We get a broken image icon if this fails. */ image = gtk_image_new_from_file( name ); return( image ); } void vfatal( GError **error ) { fprintf( stderr, PACKAGE ": fatal error\n" ); if( *error ) { fprintf( stderr, "%s\n", (*error)->message ); IM_FREEF( g_error_free, *error ); } exit( -1 ); } char * text_view_get_text( GtkTextView *text_view ) { GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkTextIter start_iter; GtkTextIter end_iter; char *text; gtk_text_buffer_get_start_iter( text_buffer, &start_iter ); gtk_text_buffer_get_end_iter( text_buffer, &end_iter ); text = gtk_text_buffer_get_text( text_buffer, &start_iter, &end_iter, FALSE ); return( text ); } void text_view_set_text( GtkTextView *text_view, const char *text, gboolean editable ) { GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); gtk_text_buffer_set_text( text_buffer, text ? text : "", -1 ); gtk_text_view_set_editable( text_view, editable ); gtk_text_view_set_cursor_visible( text_view, editable ); } void text_view_select_text( GtkTextView *text_view, int start, int end ) { GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkTextMark *mark = gtk_text_buffer_get_insert( text_buffer ); GtkTextIter start_iter; GtkTextIter end_iter; gtk_text_buffer_get_iter_at_offset( text_buffer, &start_iter, start ); gtk_text_buffer_get_iter_at_offset( text_buffer, &end_iter, end ); gtk_text_buffer_select_range( text_buffer, &start_iter, &end_iter ); gtk_text_view_scroll_mark_onscreen( text_view, mark ); } /* If parent dies, kill us too. Parent can be anything, but child must be an * iobject. */ typedef struct _DestroyIfDestroyed { GObject *child; GObject *parent; DestroyFn destroy_fn; } DestroyIfDestroyed; static void destroy_if_destroyed_parent_cb( DestroyIfDestroyed *difd, GObject *parent ); static void destroy_if_destroyed_child_cb( DestroyIfDestroyed *difd, GObject *child ); static void destroy_if_destroyed_parent_cb( DestroyIfDestroyed *difd, GObject *parent ) { GObject *child; DestroyFn destroy_fn; #ifdef DEBUG printf( "destroy_if_destroyed_parent_cb: %p\n", difd ); #endif /*DEBUG*/ /* Destroying the child will trigger the other half of difd, make sure * we remove the link first. */ child = difd->child; destroy_fn = difd->destroy_fn; g_object_weak_unref( difd->child, (GWeakNotify) destroy_if_destroyed_child_cb, difd ); destroy_fn( child ); difd->child = NULL; difd->parent = NULL; difd->destroy_fn = NULL; g_free( difd ); } static void destroy_if_destroyed_child_cb( DestroyIfDestroyed *difd, GObject *child ) { #ifdef DEBUG printf( "destroy_if_destroyed_child_cb: %p\n", difd ); #endif /*DEBUG*/ g_object_weak_unref( difd->parent, (GWeakNotify) destroy_if_destroyed_parent_cb, difd ); difd->child = NULL; difd->parent = NULL; difd->destroy_fn = NULL; g_free( difd ); } void destroy_if_destroyed( GObject *child, GObject *parent, DestroyFn destroy_fn ) { DestroyIfDestroyed *difd = g_new( DestroyIfDestroyed, 1 ); #ifdef DEBUG printf( "destroy_if_destroyed %p: parent=%p, child=%p\n", difd, parent, child ); #endif /*DEBUG*/ difd->child = child; difd->parent = parent; difd->destroy_fn = destroy_fn; g_object_weak_ref( parent, (GWeakNotify) destroy_if_destroyed_parent_cb, difd ); g_object_weak_ref( child, (GWeakNotify) destroy_if_destroyed_child_cb, difd ); } /* A 'safe' way to run a few events. */ void process_events( void ) { /* Max events we process before signalling a timeout. Without this we * can get stuck in event loops in some circumstances. */ static const int max_events = 100; /* Block too much recursion. 0 is from the top-level, 1 is from a * callback, we don't want any more than that. */ if( g_main_depth() < 2 ) { int n; #ifdef DEBUG printf( "progress_update: starting event dispatch\n" ); #endif /*DEBUG*/ for( n = 0; n < max_events && g_main_context_iteration( NULL, FALSE ); n++ ) ; #ifdef DEBUG printf( "progress_update: event dispatch done\n" ); if( n == max_events ) printf( "progress_update: event dispatch timeout\n" ); #endif /*DEBUG*/ } } ================================================ FILE: src/gtkutil.h ================================================ /* Declarations supporting gtkutil.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Look up an object's parent class dynamically. */ #define PARENT_CLASS_DYNAMIC( OBJECT ) \ (g_type_class_peek( \ g_type_parent( \ G_TYPE_FROM_INSTANCE( OBJECT ) ) )) /* Like G_CHECK_TYPE, but insist on an exact match. */ #define TYPE_EXACT( OBJECT, TYPE ) \ (G_TYPE_FROM_INSTANCE( OBJECT ) == (TYPE)) #define DESTROY_GTK( X ) { \ if( X ) { \ gtk_object_destroy( GTK_OBJECT( X ) ); \ (X) = NULL; \ } \ } void adjustments_set_value( GtkAdjustment *hadj, GtkAdjustment *vadj, float hval, float vval ); void *object_destroy( void *obj ); void *null_g_free( void *obj ); const char *object_type_name( GtkObject *obj ); void widget_visible( GtkWidget *widget, gboolean visible ); /* Make widgets. */ GtkWidget *build_button( const char *name, GtkSignalFunc cb, gpointer user ); void get_geo( GtkWidget *widget, const char *text, Rect *geo ); void set_fixed( GtkWidget *widget, int nchars ); GtkWidget *build_entry( int nchars ); GtkWidget *menu_build( const char *name ); GtkWidget *menu_add_but( GtkWidget *menu, const char *name, GtkSignalFunc cb, void *user ); GtkWidget *menu_add_tog( GtkWidget *menu, const char *name, GtkSignalFunc cb, void *user ); GtkWidget *menu_add_sep( GtkWidget *menu ); GtkWidget *menu_add_pullright( GtkWidget *popup, const char *name ); /* Popup menu handling. */ typedef void (*PopupFunc)( GtkWidget *, GtkWidget *, void * ); #define POPUP_FUNC( fn ) ((PopupFunc) (fn)) GtkWidget *popup_build( const char *name ); GtkWidget *popup_add_but( GtkWidget *, const char *, PopupFunc ); GtkWidget *popup_add_tog( GtkWidget *, const char *, PopupFunc ); GtkWidget *popup_add_pullright( GtkWidget *popup, const char *name ); void popup_show( GtkWidget *host, GdkEvent *ev ); void popup_link( GtkWidget *host, GtkWidget *popup, void *data ); guint popup_attach( GtkWidget *host, GtkWidget *popup, void *data ); void popup_detach( GtkWidget *host, guint sid ); void set_tooltip( GtkWidget *wid, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void junk_tooltips( void ); typedef void (*TooltipGenerateFn)( GtkWidget *, VipsBuf *, void *a, void *b ); void set_tooltip_generate( GtkWidget *widget, TooltipGenerateFn fn, void *a, void *b ); /* Set/get a label/entry, printf style. */ void set_gentryv( GtkWidget *edit, const char *fmt, va_list ap ); void set_gentry( GtkWidget *entry, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void set_glabel( GtkWidget *label, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void set_glabel1( GtkWidget *label, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void set_gcaption( GtkWidget *label, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); gboolean get_geditable_string( GtkWidget *text, char *out, int sz ); gboolean get_geditable_name( GtkWidget *text, char *out, int sz ); gboolean get_geditable_filename( GtkWidget *text, char *out, int sz ); gboolean get_geditable_double( GtkWidget *text, double *out ); gboolean get_geditable_int( GtkWidget *text, int *n ); gboolean get_geditable_uint( GtkWidget *text, int *n ); gboolean get_geditable_pint( GtkWidget *text, int *n ); /* Make widget groups. */ GtkWidget *build_glabelframe2( GtkWidget *box, const char *label ); GtkWidget *build_glabeltext3( GtkWidget *box, const char *label ); GtkWidget *build_glabeltext4( GtkWidget *box, GtkSizeGroup *group, const char *label ); GtkWidget *build_gtoggle( GtkWidget *box, const char *caption ); GtkWidget *build_goption( GtkWidget *box, GtkSizeGroup *group, const char *name, const char *item_names[], int nitem, GtkSignalFunc fn, void *value ); typedef gboolean (*FiledropFunc)( void *client, const char *file ); void filedrop_register( GtkWidget *widget, FiledropFunc fn, void *client ); /* Tag our thumbnail drag-n-drops with these. Start up a bit to leave room for * filedrop. */ enum { TARGET_SYMBOL = 99 }; void set_symbol_drag_type( GtkWidget *widget ); void listen_add( GObject *gobject, GObject **zap, const char *name, GCallback gcallback ); void widget_update_pointer( GtkWidget *widget, GdkEvent *ev ); void *gobject_print( GObject *gobject ); int get_dpi( void ); GtkWidget *image_new_from_file( const char *name ); void vfatal( GError **error ); char *text_view_get_text( GtkTextView *text_view ); void text_view_set_text( GtkTextView *text_view, const char *text, gboolean editable ); void text_view_select_text( GtkTextView *text_view, int start, int end ); typedef void (*DestroyFn)( GObject * ); void destroy_if_destroyed( GObject *child, GObject *parent, DestroyFn destroy_fn ); void process_events( void ); ================================================ FILE: src/heap.c ================================================ /* Heap management. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* GC on every alloc too! Extraordinarily slow. Turn on DEBUG_HEAP in ip.h * first. Good for spotting heap pointer errors. #define DEBUG_HEAP_GC */ /* Count GCs and %full, handy for tuning. #define DEBUG_GETMEM */ /* Time each GC, handy for benchmarking. #define DEBUG_GC_TIME */ #include "ip.h" static iObjectClass *parent_class = NULL; static GSList *heap_all = NULL; /* Call a function, passing in a "safe" PElement ... ie. the PElement points * at a fresh element which will be safe from the GC. */ void * heap_safe_pointer( Heap *heap, heap_safe_pointer_fn fn, void *a, void *b, void *c, void *d ) { Element e; PElement pe; void *result; e.type = ELEMENT_NOVAL; e.ele = (void *) 5; PEPOINTE( &pe, &e ); heap_register_element( heap, &e ); result = fn( heap, &pe, a, b, c, d ); heap_unregister_element( heap, &e ); return( result ); } /* Map a function over a piece of graph. */ void * heap_map( HeapNode *hn, heap_map_fn fn, void *a, void *b ) { void *c; if( !hn ) return( NULL ); switch( hn->type ) { case TAG_APPL: case TAG_CONS: if( (c = fn( hn, a, b )) ) return( c ); if( GETLT( hn ) == ELEMENT_NODE && (c = heap_map( GETLEFT( hn ), fn, a, b )) ) return( c ); if( GETRT( hn ) == ELEMENT_NODE && (c = heap_map( GETRIGHT( hn ), fn, a, b )) ) return( c ); return( NULL ); case TAG_REFERENCE: case TAG_COMPLEX: case TAG_GEN: case TAG_FILE: case TAG_CLASS: case TAG_DOUBLE: return( fn( hn, a, b ) ); case TAG_SHARED: if( (c = fn( hn, a, b )) ) return( c ); return( heap_map( GETLEFT( hn ), fn, a, b ) ); case TAG_FREE: default: g_assert( FALSE ); /* Keep gcc happy. */ return( NULL ); } } #ifdef DEBUG_HEAP_GC /* Debugging ... check that all nodes on the free list are TAG_FREE, and that * all other nodes are not TAG_FREE. */ static void heap_check_free( Heap *heap ) { HeapNode *hn; HeapBlock *hb; /* Clear all the DEBUG flags. */ for( hb = heap->hb; hb; hb = hb->next ) { int i; for( i = 0; i < hb->sz; i++ ) { HeapNode *hn = &hb->node[i]; hn->flgs &= FLAG_DEBUG ^ FLAG_ALL; } } /* Check free list. */ for( hn = heap->free; hn; hn = GETLEFT( hn ) ) { g_assert( hn->type == TAG_FREE ); hn->flgs |= FLAG_DEBUG; } /* Check for all non-free. */ for( hb = heap->hb; hb; hb = hb->next ) { int i; for( i = 0; i < hb->sz; i++ ) { HeapNode *hn = &hb->node[i]; g_assert( hn->type != TAG_FREE || (hn->flgs & FLAG_DEBUG) ); } } } #endif /*DEBUG_HEAP_GC*/ #ifdef DEBUG_HEAP_GC static void heap_check_managed( void *key, void *value, Heap *heap ) { /* Validate pointer. */ (void) MANAGED( value ); } #endif /*DEBUG_HEAP_GC*/ /* Test for sanity. */ int heap_sanity( Heap *heap ) { #ifdef DEBUG_HEAP_GC heap_check_free( heap ); heap_gc( heap ); heap_check_free( heap ); g_hash_table_foreach( heap->mtable, (GHFunc) heap_check_managed, heap ); #endif /*DEBUG_HEAP_GC*/ return( 0 ); } /* Debugging ... check that all heaps have been closed, dump any which * haven't. */ void heap_check_all_destroyed( void ) { slist_map( heap_all, (SListMapFn) iobject_dump, NULL ); } /* Free a HeapBlock. */ static void heapblock_free( HeapBlock *hb ) { #ifdef DEBUG printf( "heapblock_free\n" ); #endif /*DEBUG*/ if( hb->next ) heapblock_free( hb->next ); if( hb->node ) IM_FREE( hb->node ); IM_FREE( hb ); } static void heap_set_flush( Heap *heap, gboolean flush ) { heap->flush = flush; } static void heap_dispose_print( void *key, void *value ) { Managed *managed = MANAGED( value ); iobject_print( IOBJECT( managed ) ); } static void heap_dispose( GObject *gobject ) { Heap *heap = HEAP( gobject ); /* Repeatedly close managed objects. Each close can trigger other * closes, so we need to loop until done. */ managed_clear( heap ); heap_set_flush( heap, TRUE ); while( managed_free_unused( heap ) ) ; /* Check all managed objects are dead. */ g_hash_table_foreach( heap->mtable, (GHFunc) heap_dispose_print, NULL ); IM_FREEF( g_source_remove, heap->gc_tid ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void heap_finalize( GObject *gobject ) { Heap *heap = HEAP( gobject ); if( heap->hb ) heapblock_free( heap->hb ); IM_FREEF( g_hash_table_destroy, heap->emark ); IM_FREEF( g_hash_table_destroy, heap->rmark ); IM_FREEF( g_hash_table_destroy, heap->mtable ); heap_all = g_slist_remove( heap_all, heap ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void heap_info( iObject *iobject, VipsBuf *buf ) { Heap *heap = HEAP( iobject ); vips_buf_appendf( buf, "compile = " ); if( heap->compile ) if( heap->compile->sym ) { symbol_qualified_name( heap->compile->sym, buf ); vips_buf_appendf( buf, "(%p) (sym)\n", heap->compile->sym ); } else vips_buf_appendf( buf, "(compile, but no sym)\n" ); else vips_buf_appendf( buf, "(no compile)\n" ); vips_buf_appendf( buf, "mxb (max blocks) = %d\n", heap->mxb ); vips_buf_appendf( buf, "rsz (nodes per block) = %d\n", heap->rsz ); vips_buf_appendf( buf, "nb (number of blocks) = %d\n", heap->nb ); vips_buf_appendf( buf, "emark = %d pointers\n", g_hash_table_size( heap->emark ) ); vips_buf_appendf( buf, "rmark = %d pointers\n", g_hash_table_size( heap->rmark ) ); vips_buf_appendf( buf, "ncells (cells allocated) = %d\n", heap->ncells ); vips_buf_appendf( buf, "nfree (cells free at last GC) = %d\n", heap->nfree ); vips_buf_appendf( buf, "mtable (Managed blocks) = %d pointers\n", g_hash_table_size( heap->mtable ) ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); } /* Empty a heap block. */ static void heapblock_empty( HeapBlock *hb ) { int i; /* Set as empty free-list. */ for( i = 0; i < hb->sz; i++ ) { HeapNode *hn = &hb->node[i]; hn->type = TAG_FREE; hn->flgs = 0; PPUTLEFT( hn, ELEMENT_NODE, hn + 1 ); } PPUTLEFT( &hb->node[hb->sz - 1], ELEMENT_NODE, NULL ); } /* Add another HeapBlock, if we can. */ static gboolean heapblock_create( Heap *heap, int sz ) { HeapBlock *hb; if( heap->nb > heap->mxb ) { heap->mxb = 1 + (heap->max_fn( heap ) / heap->rsz); if( heap->nb > heap->mxb ) /* Hit limit ... caller detects full by ->free becomng * NULL. */ return( TRUE ); } #ifdef DEBUG printf( "heapblock_create: new block, size %d\n", sz ); #endif /*DEBUG*/ if( !(hb = INEW( NULL, HeapBlock )) ) return( FALSE ); hb->heap = heap; hb->next = NULL; hb->node = NULL; hb->sz = sz; if( !(hb->node = IARRAY( NULL, sz, HeapNode )) ) { heapblock_free( hb ); return( FALSE ); } heapblock_empty( hb ); /* Link to existing blocks. */ hb->next = heap->hb; heap->hb = hb; PPUTLEFT( &hb->node[hb->sz - 1], ELEMENT_NODE, heap->free ); heap->free = &hb->node[0]; heap->nb++; return( TRUE ); } static void heap_class_init( HeapClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = heap_dispose; gobject_class->finalize = heap_finalize; iobject_class->info = heap_info; } static void heap_init( Heap *heap ) { heap->compile = NULL; heap->max_fn = NULL; heap->mxb = -1; heap->rsz = 0; heap->nb = 0; heap->hb = NULL; heap->free = NULL; heap->ncells = 0; heap->nfree = 0; heap->serial = 0; heap->filled = FALSE; heap->emark = g_hash_table_new( NULL, g_direct_equal ); heap->rmark = g_hash_table_new( NULL, g_direct_equal ); heap->mtable = g_hash_table_new( NULL, g_direct_equal ); heap->gc_tid = 0; heap->flush = FALSE; heap_all = g_slist_prepend( heap_all, heap ); } GType heap_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( HeapClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) heap_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Heap ), 32, /* n_preallocs */ (GInstanceInitFunc) heap_init, }; type = g_type_register_static( TYPE_IOBJECT, "Heap", &info, 0 ); } return( type ); } static void heap_link( Heap *heap, Compile *compile, heap_max_fn max_fn, int stsz, int rsz ) { heap->compile = compile; heap->max_fn = max_fn; heap->rsz = rsz; (void) heapblock_create( heap, stsz ); if( compile ) iobject_set( IOBJECT( heap ), IOBJECT( compile->sym )->name, NULL ); /* Can now set max blocks. */ heap->mxb = 1 + (heap->max_fn( heap ) / rsz); } /* Create an empty heap. mxsz is maximum size of heap in units of nodes, * stsz is start size, rsz is heap growth unit. */ Heap * heap_new( Compile *compile, heap_max_fn max_fn, int stsz, int rsz ) { Heap *heap; heap = HEAP( g_object_new( TYPE_HEAP, NULL ) ); heap_link( heap, compile, max_fn, stsz, rsz ); return( heap ); } /* Set flags on a heap. */ void heap_set( Heap *heap, NodeFlags setmask ) { HeapBlock *hb; int i; for( hb = heap->hb; hb; hb = hb->next ) for( i = 0; i < hb->sz; i++ ) hb->node[i].flgs |= setmask; } /* Clear flags on a heap. */ void heap_clear( Heap *heap, NodeFlags clearmask ) { HeapBlock *hb; int i; int cmask = clearmask ^ FLAG_ALL; for( hb = heap->hb; hb; hb = hb->next ) for( i = 0; i < hb->sz; i++ ) hb->node[i].flgs &= cmask; } /* Allocate a new serial number for a heap. On return, we guarantee that * heap->serial is a value not used by any nodes in the heap. */ int heap_serial_new( Heap *heap ) { heap->serial += 1; if( heap->serial > FLAG_SERIAL ) { heap->serial = 1; heap_clear( heap, FLAG_SERIAL ); } return( heap->serial ); } /* Mark a tree. Avoid recursion because of the danger of C stack overflow on * large heaps. */ static void heap_mark_tree( Heap *heap, HeapNode *hn ) { GSList *pending = NULL; pending = g_slist_prepend( pending, hn ); while( pending ) { hn = (HeapNode *) pending->data; pending = g_slist_remove( pending, hn ); /* Chase down the LHS of the nodes, add the RHS nodes we pass * to the pending list. */ for(;;) { if( hn->flgs & FLAG_MARK ) break; hn->flgs |= FLAG_MARK; /* Don't modify hn for the do-nothing case: we'll * break on the next loop. */ switch( hn->type ) { case TAG_GEN: case TAG_COMPLEX: case TAG_CLASS: case TAG_APPL: case TAG_CONS: if( GETRT( hn ) == ELEMENT_MANAGED ) managed_mark( (Managed *) GETRIGHT( hn ) ); if( GETLT( hn ) == ELEMENT_MANAGED ) managed_mark( (Managed *) GETLEFT( hn ) ); if( GETRT( hn ) == ELEMENT_NODE ) { if( GETLT( hn ) == ELEMENT_NODE ) { pending = g_slist_prepend( pending, GETRIGHT( hn ) ); hn = GETLEFT( hn ); } else hn = GETRIGHT( hn ); } else if( GETLT( hn ) == ELEMENT_NODE ) hn = GETLEFT( hn ); break; case TAG_FILE: g_assert( GETLT( hn ) == ELEMENT_MANAGED ); managed_mark( (Managed *) GETLEFT( hn ) ); break; case TAG_DOUBLE: break; case TAG_SHARED: case TAG_REFERENCE: if( GETLT( hn ) == ELEMENT_NODE ) hn = GETLEFT( hn ); break; case TAG_FREE: default: g_assert( FALSE ); } } } } /* Mark an element. */ static void * mark_pelement( PElement *base, Heap *heap ) { if( PEISMANAGED( base ) ) managed_mark( MANAGED( PEGETVAL( base ) ) ); else if( PEISNODE( base ) ) heap_mark_tree( heap, PEGETVAL( base ) ); return( NULL ); } /* Mark an element. */ static void mark_element( void *key, void *value, Heap *heap ) { Element *root = (Element *) value; PElement base; PEPOINTE( &base, root ); (void) mark_pelement( &base, heap ); } /* Mark a reduce context ... the heapnodes on the spine stack etc. */ static void * mark_reduce( void *key, void *value, Heap *heap ) { Reduce *rc = (Reduce *) value; int i; #ifdef DEBUG printf( "mark_reduce: marking %d stack elements\n", rc->sp ); #endif /*DEBUG*/ for( i = 0; i < rc->sp; i++ ) heap_mark_tree( heap, rc->nstack[i] ); return( NULL ); } /* Do a garbage collect. */ gboolean heap_gc( Heap *heap ) { HeapBlock *hb; int nfree; int ncells; int nblocks; #ifdef DEBUG_GC_TIME static GTimer *GC_timer = NULL; if( !GC_timer ) GC_timer = g_timer_new(); g_timer_reset( GC_timer ); printf( "heap_gc: starting GC for heap %s\n", IOBJECT( heap )->name ); #endif /*DEBUG_GC_TIME*/ /* Clear marks on managed objects. Nodes should all be clear already. */ managed_clear( heap ); /* All flags should be clear, so just mark. */ g_hash_table_foreach( heap->emark, (GHFunc) mark_element, heap ); g_hash_table_foreach( heap->rmark, (GHFunc) mark_reduce, heap ); /* And sweep up unmarked into new free list. */ heap->free = NULL; ncells = nfree = nblocks = 0; for( hb = heap->hb; hb; hb = hb->next ) { const int sz = hb->sz; int i; for( i = 0; i < sz; i++ ) { HeapNode * const hn = &hb->node[i]; if( !(hn->flgs & FLAG_MARK) ) { hn->type = TAG_FREE; PPUTLEFT( hn, ELEMENT_NODE, heap->free ); #ifdef DEBUG_HEAP_GC /* Not necessary, but may be helpful to zap * any pointer in there. */ PPUTRIGHT( hn, ELEMENT_NODE, NULL ); #endif /*DEBUG_HEAP_GC*/ heap->free = hn; nfree += 1; } hn->flgs &= FLAG_MARK ^ FLAG_ALL; } ncells += hb->sz; nblocks += 1; } heap->ncells = ncells; heap->nfree = nfree; /* Close unused managed objects. It can (potentially) take a couple of * passes through mtable to free everything ... but we'll do more on * the next GC. */ managed_free_unused( heap ); #ifdef DEBUG_GC_TIME printf( "heap_gc: %d cells in %d blocks, %d in use\n", ncells, nblocks, ncells - nfree ); printf( "(GC took %gs)\n", g_timer_elapsed( GC_timer, NULL ) ); #endif /*DEBUG_GC_TIME*/ return( TRUE ); } static gint heap_gc_request_cb( Heap *heap ) { heap->gc_tid = 0; if( !heap_gc( heap ) ) printf( "help! delayed GC failed!\n" ); iobject_changed( IOBJECT( heap ) ); return( FALSE ); } /* Request a delayed garbage collect. */ void heap_gc_request( Heap *heap ) { IM_FREEF( g_source_remove, heap->gc_tid ); heap->gc_tid = g_timeout_add( 1000, (GSourceFunc) heap_gc_request_cb, heap ); } /* Register a pointer into a heap. */ void heap_register_element( Heap *heap, Element *root ) { g_hash_table_insert( heap->emark, root, root ); } /* Unregister a pointer into a heap. */ void heap_unregister_element( Heap *heap, Element *root ) { if( g_hash_table_remove( heap->emark, root ) ) { #ifdef DEBUG printf( "heap_unregister_element: %d pointers\n", g_hash_table_size( heap->emark ) ); #endif } } /* Register a Reduce working on this heap. */ void heap_register_reduce( Heap *heap, Reduce *rc ) { g_hash_table_insert( heap->rmark, rc, rc ); } /* Unregister a reduce context. */ void heap_unregister_reduce( Heap *heap, Reduce *rc ) { g_hash_table_remove( heap->rmark, rc ); } /* Allocate a new HeapNode ... long version. See NEWNODE() macro. */ HeapNode * heap_getmem( Heap *heap ) { HeapNode *hn; int pcused; #ifdef DEBUG_GETMEM static int n_heap_getmem = 0; #endif /*DEBUG_GETMEM*/ /* Easy case ... this should be handled by the NEWNODE macro, but do * it here as well just in case. */ if( heap->free ) { (void) EXTRACTNODE( heap, hn ); return( hn ); } #ifdef DEBUG printf( "heap_getmem: GC on full heap for heap %s\n", IOBJECT( heap )->name ); #endif /*DEBUG*/ /* Try a GC. */ if( !heap_gc( heap ) ) return( NULL ); /* Is heap over x% full? Add another heap block if we can. */ pcused = 100 * (heap->ncells - heap->nfree) / heap->ncells; #ifdef DEBUG_GETMEM n_heap_getmem += 1; printf( "heap_getmem: %d%% (%d)\n", pcused, n_heap_getmem ); #endif /*DEBUG_GETMEM*/ if( pcused > 50 ) { int nblocks = 1 + (heap->ncells - heap->nfree) / heap->rsz; int i; #ifdef DEBUG_GETMEM printf( "heap_getmem: %d more blocks added\n", nblocks ); #endif /*DEBUG_GETMEM*/ for( i = 0; i < nblocks; i++ ) if( !heapblock_create( heap, heap->rsz ) ) return( NULL ); } if( !heap->free ) { error_top( _( "Heap full." ) ); if( heap->compile ) { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); compile_name( heap->compile, &buf ); error_sub( _( "The compile heap for %s has filled. " "Make it smaller and less complicated." ), vips_buf_all( &buf ) ); } else error_sub( _( "The main calculation heap has filled. " "Raise the heap size limit in Preferences." ) ); heap->filled = TRUE; return( NULL ); } (void) EXTRACTNODE( heap, hn ); return( hn ); } gboolean heap_bool_new( Heap *heap, gboolean val, PElement *out ) { PEPUTP( out, ELEMENT_BOOL, val ); return( TRUE ); } /* Write a real to an element. */ gboolean heap_real_new( Heap *heap, double in, PElement *out ) { HeapNode *hn; if( NEWNODE( heap, hn ) ) return( FALSE ); hn->type = TAG_DOUBLE; hn->body.num = in; PEPUTP( out, ELEMENT_NODE, hn ); return( TRUE ); } /* Write an element to an element. */ gboolean heap_element_new( Heap *heap, Element *e, PElement *out ) { PEPUTE( out, e ); return( TRUE ); } /* Make a complex node from two elements. */ gboolean heap_complex_element_new( Heap *heap, PElement *rp, PElement *ip, PElement *out ) { HeapNode *hn; if( NEWNODE( heap, hn ) ) return( FALSE ); hn->type = TAG_COMPLEX; PPUT( hn, PEGETTYPE( rp ), PEGETVAL( rp ), PEGETTYPE( ip ), PEGETVAL( ip ) ); PEPUTP( out, ELEMENT_NODE, hn ); return( TRUE ); } /* Make a complex node. */ gboolean heap_complex_new( Heap *heap, double rp, double ip, PElement *out ) { Element dummy; PElement t; /* Form complex node. */ dummy.type = ELEMENT_NOVAL; dummy.ele = (void *) 6; PEPOINTE( &t, &dummy ); if( !heap_complex_element_new( heap, &t, &t, out ) ) return( FALSE ); /* Install real and imag parts. */ PEPOINTLEFT( PEGETVAL( out ), &t ); if( !heap_real_new( heap, rp, &t ) ) return( FALSE ); PEPOINTRIGHT( PEGETVAL( out ), &t ); if( !heap_real_new( heap, ip, &t ) ) return( FALSE ); return( TRUE ); } /* 'get' a list: move the PE to point at the list. */ gboolean heap_get_list( PElement *list ) { g_assert( PEISLIST( list ) ); if( PEISMANAGEDSTRING( list ) ) { if( !managedstring_get( PEGETMANAGEDSTRING( list ), list ) ) return( FALSE ); } return( TRUE ); } /* Set list to []. */ void heap_list_init( PElement *list ) { PEPUTP( list, ELEMENT_ELIST, NULL ); } /* Add new node to list, point data at new CONS LHS. */ gboolean heap_list_add( Heap *heap, PElement *list, PElement *data ) { HeapNode *hn; /* Build CONS node. */ if( NEWNODE( heap, hn ) ) return( FALSE ); hn->type = TAG_CONS; PPUTLEFT( hn, ELEMENT_NOVAL, (void *) 7 ); PEPUTRIGHT( hn, list ); PEPUTP( list, ELEMENT_NODE, hn ); /* Point data to new LHS. */ PEPOINTLEFT( hn, data ); return( TRUE ); } /* Move list on to the next RHS. list points at [], or pointer to next node. * Used with heap_list_init()/heap_list_add() to build lists. */ gboolean heap_list_next( PElement *list ) { HeapNode *hn = PEGETVAL( list ); if( hn ) { PEPOINTRIGHT( hn, list ); return( TRUE ); } else return( FALSE ); } gboolean heap_list_cat( Reduce *rc, PElement *a, PElement *b, PElement *out ) { PElement list = *out; REDUCE_CATCH_START( FALSE ); reduce_clone_list( rc, a, &list ); PEPUTPE( &list, b ); REDUCE_CATCH_STOP; return( TRUE ); } /* Start off a function application. */ void heap_appl_init( PElement *base, PElement *func ) { PEPUTPE( base, func ); } /* Add a new parameter to a function application. base points at the * function built so far ... update base to point to new node (old base * becomes LHS), return parm pointing to new RHS */ gboolean heap_appl_add( Heap *heap, PElement *base, PElement *parm ) { HeapNode *hn; /* Build appl node. */ if( NEWNODE( heap, hn ) ) return( FALSE ); hn->type = TAG_APPL; PEPUTLEFT( hn, base ); PPUTRIGHT( hn, ELEMENT_ELIST, NULL ); PEPUTP( base, ELEMENT_NODE, hn ); /* Point parm to new RHS. */ PEPOINTRIGHT( hn, parm ); return( TRUE ); } /* Make a lazy file read node. */ gboolean heap_file_new( Heap *heap, const char *filename, PElement *out ) { Managedfile *managedfile; HeapNode *hn; if( !(managedfile = managedfile_new( heap, filename )) ) return( FALSE ); /* Make sure the managedfile survives a GC. */ MANAGED_REF( managedfile ); if( NEWNODE( heap, hn ) ) { MANAGED_UNREF( managedfile ); return( FALSE ); } hn->type = TAG_FILE; PPUT( hn, ELEMENT_MANAGED, managedfile, ELEMENT_ELIST, NULL ); PEPUTP( out, ELEMENT_NODE, hn ); MANAGED_UNREF( managedfile ); return( TRUE ); } /* Make a heap string. */ gboolean heap_string_new( Heap *heap, const char *str, PElement *out ) { PElement list = *out; const int n = strlen( str ); int i; heap_list_init( &list ); for( i = 0; i < n; i++ ) { PElement t; if( !heap_list_add( heap, &list, &t ) ) return( FALSE ); PEPUTP( &t, ELEMENT_CHAR, (int) str[i] ); (void) heap_list_next( &list ); } return( TRUE ); } /* Make a managed string. */ gboolean heap_managedstring_new( Heap *heap, const char *str, PElement *out ) { Managedstring *managedstring; if( strcmp( str, "" ) == 0 ) { PEPUTP( out, ELEMENT_ELIST, NULL ); } else { if( !(managedstring = managedstring_find( heap, str )) ) return( FALSE ); PEPUTP( out, ELEMENT_MANAGED, managedstring ); } return( TRUE ); } /* Make a [[char]]. */ gboolean heap_lstring_new( Heap *heap, GSList *labels, PElement *out ) { PElement list = *out; const int n = g_slist_length( labels ); int i; /* Make first RHS ... the end of the list. */ heap_list_init( &list ); /* Build a CONS node for each element. */ for( i = 0; i < n; i++ ) { PElement t; if( !heap_list_add( heap, &list, &t ) || !heap_managedstring_new( heap, g_slist_nth_data( labels, i ), &t ) ) return( FALSE ); (void) heap_list_next( &list ); } return( TRUE ); } /* Make a realvec. */ gboolean heap_realvec_new( Heap *heap, int n, double *vec, PElement *out ) { PElement list = *out; int i; /* Make first RHS ... the end of the list. */ heap_list_init( &list ); /* Build a CONS node for each element. */ for( i = 0; i < n; i++ ) { PElement t; if( !heap_list_add( heap, &list, &t ) ) return( FALSE ); if( !heap_real_new( heap, vec[i], &t ) ) return( FALSE ); (void) heap_list_next( &list ); } return( TRUE ); } /* Make a realvec, but from an int*. */ gboolean heap_intvec_new( Heap *heap, int n, int *vec, PElement *out ) { PElement list = *out; int i; /* Make first RHS ... the end of the list. */ heap_list_init( &list ); /* Build a CONS node for each element. */ for( i = 0; i < n; i++ ) { PElement t; if( !heap_list_add( heap, &list, &t ) ) return( FALSE ); if( !heap_real_new( heap, (double) vec[i], &t ) ) return( FALSE ); (void) heap_list_next( &list ); } return( TRUE ); } /* Make a matrix. */ gboolean heap_matrix_new( Heap *heap, int xsize, int ysize, double *vec, PElement *out ) { PElement list = *out; int y, i; /* Make first RHS ... the end of the list. */ heap_list_init( &list ); /* Build a CONS node for each element. */ for( i = 0, y = 0; y < ysize; y++ ) { PElement t; if( !heap_list_add( heap, &list, &t ) ) return( FALSE ); if( !heap_realvec_new( heap, xsize, vec + i, &t ) ) return( FALSE ); i += xsize; (void) heap_list_next( &list ); } return( TRUE ); } /* Make a typecheck error. Always return FALSE ... the gboolean is just there * for REDUCE_CATCH. */ gboolean heap_error_typecheck( PElement *e, const char *name, const char *type ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); (void) reduce_error_typecheck( reduce_context, e, name, type ); REDUCE_CATCH_STOP; return( FALSE ); } /* Map over a heap list. Reduce the list spine as we go, don't reduce the * heads. Return base on error, or whatever the user function returns (unlike * reduce_map_list(), which we can't just wrap). */ void * heap_map_list( PElement *base, heap_map_list_fn fn, void *a, void *b ) { Reduce *rc = reduce_context; PElement e = *base; if( !reduce_pelement( rc, reduce_spine, &e ) ) return( base ); if( !PEISLIST( &e ) ) { heap_error_typecheck( &e, "heap_map_list", "[*]" ); return( base ); } while( PEISFLIST( &e ) ) { PElement head; void *res; if( !heap_get_list( &e ) ) return( base ); /* Apply user function to the head. */ PEGETHD( &head, &e ); if( (res = fn( &head, a, b )) ) return( res ); /* Reduce the tail. */ PEGETTL( &e, &e ); if( !reduce_pelement( rc, reduce_spine, &e ) ) return( base ); } return( NULL ); } /* Iterate over a list. Move list on to the next tl, point data at the * head of the current node, FALSE for []. */ gboolean heap_get_list_next( PElement *list, PElement *data ) { Reduce *rc = reduce_context; if( !reduce_pelement( rc, reduce_spine, list ) ) return( FALSE ); if( PEISFLIST( list ) ) { HeapNode *hn; if( !heap_get_list( list ) ) return( FALSE ); hn = PEGETVAL( list ); PEPOINTRIGHT( hn, list ); PEPOINTLEFT( hn, data ); return( TRUE ); } else return( FALSE ); } typedef struct _HeapMapDict { heap_map_dict_fn fn; void *a; void *b; } HeapMapDict; static void * heap_map_dict_entry( PElement *head, HeapMapDict *map_dict ) { Reduce *rc = reduce_context; char key[256]; PElement p1, p2; void *result; if( !reduce_pelement( rc, reduce_spine, head ) ) return( head ); if( !PEISFLIST( head ) ) { heap_error_typecheck( head, "heap_map_dict", "[*]" ); return( head ); } if( !heap_get_list( head ) ) return( head ); PEGETHD( &p1, head ); if( !heap_get_string( &p1, key, 256 ) ) return( head ); PEGETTL( &p2, head ); if( !reduce_pelement( rc, reduce_spine, &p2 ) ) return( head ); if( !PEISFLIST( &p2 ) ) { heap_error_typecheck( &p2, "heap_map_dict", "[*]" ); return( head ); } if( !heap_get_list( &p2 ) ) return( head ); PEGETHD( &p1, &p2 ); if( (result = map_dict->fn( key, &p1, map_dict->a, map_dict->b )) ) return( result ); PEGETTL( &p1, &p2 ); if( !reduce_pelement( rc, reduce_spine, &p1 ) ) return( head ); if( !PEISELIST( &p1 ) ) { heap_error_typecheck( &p1, "heap_map_dict", "[]" ); return( head ); } return( NULL ); } /* Map over a list of ["key", value] pairs. */ void * heap_map_dict( PElement *base, heap_map_dict_fn fn, void *a, void *b ) { HeapMapDict map_dict; map_dict.fn = fn; map_dict.a = a; map_dict.b = b; return( heap_map_list( base, (heap_map_list_fn) heap_map_dict_entry, &map_dict, NULL ) ); } /* Evaluate a PElement into a string buffer. */ gboolean heap_get_string( PElement *base, char *buf, int n ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); (void) reduce_get_string( reduce_context, base, buf, n ); REDUCE_CATCH_STOP; return( TRUE ); } /* Evaluate a PElement to a [[char]]. */ gboolean heap_get_lstring( PElement *base, GSList **labels ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); (void) reduce_get_lstring( reduce_context, base, labels ); REDUCE_CATCH_STOP; return( TRUE ); } /* Get an element as a bool. */ gboolean heap_get_bool( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_get_bool( reduce_context, base ); REDUCE_CATCH_STOP; return( TRUE ); } /* Get an element as a real. */ gboolean heap_get_real( PElement *base, double *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_get_real( reduce_context, base ); REDUCE_CATCH_STOP; return( TRUE ); } /* Get an element as a class ... just reduce and typecheck. */ gboolean heap_get_class( PElement *base, PElement *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); reduce_get_class( reduce_context, base ); REDUCE_CATCH_STOP; /* Point out at base ... for consistency with other getters. */ *out = *base; return( TRUE ); } /* Get an element as an image. */ gboolean heap_get_image( PElement *base, Imageinfo **out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_get_image( reduce_context, base ); REDUCE_CATCH_STOP; return( TRUE ); } /* Get an element as a realvec. Return -1 on error, or length of vector. */ int heap_get_realvec( PElement *base, double *buf, int n ) { Reduce *rc = reduce_context; int l; REDUCE_CATCH_START( -1 ); l = reduce_get_realvec( reduce_context, base, buf, n ); REDUCE_CATCH_STOP; return( l ); } /* Get an element as a imagevec. Return -1 on error, or length of vector. */ int heap_get_imagevec( PElement *base, Imageinfo **buf, int n ) { Reduce *rc = reduce_context; int l; REDUCE_CATCH_START( -1 ); l = reduce_get_imagevec( reduce_context, base, buf, n ); REDUCE_CATCH_STOP; return( l ); } /* Get an element as a matrix. Return -1 on error, or length of buffer used. * Write xsize/ysize to args. */ gboolean heap_get_matrix_size( PElement *base, int *xsize, int *ysize ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); (void) reduce_get_matrix_size( reduce_context, base, xsize, ysize ); REDUCE_CATCH_STOP; return( TRUE ); } /* Get an element as a matrix. Return -1 on error, or length of buffer used. * Write xsize/ysize to args. */ gboolean heap_get_matrix( PElement *base, double *buf, int n, int *xsize, int *ysize ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); (void) reduce_get_matrix( reduce_context, base, buf, n, xsize, ysize ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_elist( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_elist( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_list( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_list( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } /* Do a get, check it's OK. We don't get very much, in case it's a long * string and will take a while to eval. */ gboolean heap_is_string( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_string( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_realvec( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_realvec( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_imagevec( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_imagevec( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_matrix( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_matrix( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_class( PElement *base, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_class( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_instanceof_exact( const char *name, PElement *klass, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_instanceof_exact( rc, name, klass ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_is_instanceof( const char *name, PElement *klass, gboolean *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); *out = reduce_is_instanceof( rc, name, klass ); REDUCE_CATCH_STOP; return( TRUE ); } int heap_list_length( PElement *base ) { Reduce *rc = reduce_context; int result; REDUCE_CATCH_START( -1 ); result = reduce_list_length( rc, base ); REDUCE_CATCH_STOP; return( result ); } int heap_list_length_max( PElement *base, int max_length ) { Reduce *rc = reduce_context; int result; REDUCE_CATCH_START( -1 ); result = reduce_list_length_max( rc, base, max_length ); REDUCE_CATCH_STOP; return( result ); } gboolean heap_list_index( PElement *base, int n, PElement *out ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); reduce_list_index( rc, base, n, out ); REDUCE_CATCH_STOP; return( TRUE ); } gboolean heap_reduce_strict( PElement *base ) { Reduce *rc = reduce_context; REDUCE_CATCH_START( FALSE ); reduce_spine_strict( rc, base ); REDUCE_CATCH_STOP; return( TRUE ); } /* hn is a node in a compiled function, out is part of a node in reduce * space to which it should be copied. * * Have to be careful to copy sym pointers in nodes from compile heap. */ static gboolean copy_node( Heap *heap, HeapNode *ri[], HeapNode *hn, PElement *out ) { HeapNode *hn1; PElement pleft, pright; int i; /* Look for relocation nodes. */ if( hn->type == TAG_SHARED ) { /* RHS of SHARE is the index of this share node. */ i = GPOINTER_TO_INT( GETRIGHT( hn ) ); /* Skip to shared section. */ hn = GETLEFT( hn ); /* Copy and link on this node. */ if( NEWNODE( heap, hn1 ) ) return( FALSE ); *hn1 = *hn; PEPUTP( out, ELEMENT_NODE, hn1 ); /* Note pointer in relocation table. */ ri[i] = hn1; } else if( hn->type == TAG_REFERENCE ) { /* Must have already copied this SHARE, just link back. */ hn1 = GETLEFT( hn ); i = GPOINTER_TO_INT( GETRIGHT( hn1 ) ); PEPUTP( out, ELEMENT_NODE, ri[i] ); /* Done! */ return( TRUE ); } else { /* Copy and link on this node. */ if( NEWNODE( heap, hn1 ) ) return( FALSE ); *hn1 = *hn; PEPUTP( out, ELEMENT_NODE, hn1 ); } /* If it's a DOUBLE, no more to do. */ if( hn->type == TAG_DOUBLE ) return( TRUE ); if( hn->ltype != ELEMENT_NODE && hn->rtype == ELEMENT_NODE ) { /* Right pointer only. Zap pointer so we can GC * safely. */ hn1->rtype = ELEMENT_CHAR; /* Recurse for RHS of node. */ PEPOINTRIGHT( hn1, &pright ); if( !copy_node( heap, ri, GETRIGHT( hn ), &pright ) ) return( FALSE ); } else if( hn->ltype == ELEMENT_NODE && hn->rtype != ELEMENT_NODE ) { /* Left pointer only. Zap pointer so we can GC * safely. */ hn1->ltype = ELEMENT_CHAR; /* Recurse for LHS of node. */ PEPOINTLEFT( hn1, &pleft ); if( !copy_node( heap, ri, GETLEFT( hn ), &pleft ) ) return( FALSE ); } else if( hn->ltype == ELEMENT_NODE && hn->rtype == ELEMENT_NODE ) { /* Both pointers. Zap pointers so we can GC safely. */ hn1->ltype = ELEMENT_CHAR; hn1->rtype = ELEMENT_CHAR; /* Recurse for boths sides of node. */ PEPOINTLEFT( hn1, &pleft ); PEPOINTRIGHT( hn1, &pright ); if( !copy_node( heap, ri, GETLEFT( hn ), &pleft ) || !copy_node( heap, ri, GETRIGHT( hn ), &pright ) ) return( FALSE ); } return( TRUE ); } /* Copy a compiled graph into the main reduce space. Overwrite the node at * out. */ gboolean heap_copy( Heap *heap, Compile *compile, PElement *out ) { Element *root = &compile->base; HeapNode *ri[MAX_RELOC]; /* Check for possible C stack overflow ... can't go over 2M on most * systems if we're using (or any of our libs are using) threads. */ if( (char *) main_c_stack_base - (char *) &heap > 2000000 ) { error_top( _( "Overflow error." ) ); error_sub( _( "C stack overflow. Circular definition." ) ); return( FALSE ); } #ifdef DEBUG printf( "heap_copy: " ); symbol_name_print( compile->sym ); printf( "\n" ); #endif /*DEBUG*/ /* Check for possible C stack overflow ... can't go over 2M on most * systems if we're using (or any of our libs are using) threads. */ if( (char *) main_c_stack_base - (char *) &heap > 2000000 ) { error_top( _( "Overflow error." ) ); error_sub( _( "C stack overflow. Expression too complex." ) ); return( FALSE ); } switch( root->type ) { case ELEMENT_NODE: /* Need a tree copy. */ if( !copy_node( heap, &ri[0], (HeapNode *) root->ele, out ) ) return( FALSE ); break; case ELEMENT_SYMBOL: case ELEMENT_CHAR: case ELEMENT_BOOL: case ELEMENT_BINOP: case ELEMENT_SYMREF: case ELEMENT_COMPILEREF: case ELEMENT_CONSTRUCTOR: case ELEMENT_UNOP: case ELEMENT_COMB: case ELEMENT_TAG: case ELEMENT_ELIST: case ELEMENT_MANAGED: /* Copy value. */ PEPUTP( out, root->type, root->ele ); break; case ELEMENT_NOVAL: /* Not compiled yet: compile now, then copy. */ if( compile_object( compile ) ) return( FALSE ); if( !heap_copy( heap, compile, out ) ) return( FALSE ); break; default: g_assert( FALSE ); } return( TRUE ); } /* Try to make a gvalue from a heap object. */ gboolean heap_ip_to_gvalue( PElement *in, GValue *out ) { Reduce *rc = reduce_context; if( !reduce_pelement( rc, reduce_spine_strict, in ) ) return( FALSE ); if( PEISREAL( in ) ) { g_value_init( out, G_TYPE_DOUBLE ); g_value_set_double( out, PEGETREAL( in ) ); } else if( PEISBOOL( in ) ) { g_value_init( out, G_TYPE_BOOLEAN ); g_value_set_boolean( out, PEGETBOOL( in ) ); } else if( PEISCOMPLEX( in ) ) { printf( "ip_to_gvalue: no complex gtype!\n" ); return( FALSE ); } else if( PEISIMAGE( in ) ) { Imageinfo *ii = PEGETII( in ); VipsImage *im = imageinfo_get( FALSE, ii ); g_value_init( out, VIPS_TYPE_IMAGE ); g_value_set_object( out, im ); } else if( PEISLIST( in ) ) { gboolean result; if( heap_is_string( in, &result ) && result ) { char name[256]; if( !heap_get_string( in, name, 256 ) ) return( FALSE ); /* We want a refstring, not a G_TYPE_STRING, since * this GValue will (probably) be used by vips with * im_header_string() etc. */ g_value_init( out, IM_TYPE_REF_STRING ); im_ref_string_set( out, name ); } #if VIPS_MAJOR_VERSION > 7 || VIPS_MINOR_VERSION > 39 /* vips_value_set_array_*() is a 7.40 feature. */ else if( heap_is_imagevec( in, &result ) && result ) { Imageinfo *iivec[MAX_VEC]; VipsImage **ivec; int n; int i; if( (n = heap_get_imagevec( in, iivec, MAX_VEC )) < 0 ) return( FALSE ); g_value_init( out, VIPS_TYPE_ARRAY_IMAGE ); vips_value_set_array_image( out, n ); ivec = vips_value_get_array_image( out, NULL ); for( i = 0; i < n; i++ ) { ivec[i] = imageinfo_get( FALSE, iivec[i] ); /* g_value_unset() on out will unref every * array element, so we need to ref. */ g_object_ref( ivec[i] ); } } else if( heap_is_realvec( in, &result ) && result ) { double realvec[MAX_VEC]; int n; if( (n = heap_get_realvec( in, realvec, MAX_VEC )) < 0 ) return( FALSE ); g_value_init( out, VIPS_TYPE_ARRAY_DOUBLE ); vips_value_set_array_double( out, realvec, n ); } #endif else { error_top( _( "Unimplemented list type." ) ); return( FALSE ); } } else if( PEISMANAGED( in ) && IS_MANAGEDGOBJECT( PEGETVAL( in ) ) ) { g_value_init( out, G_TYPE_OBJECT ); g_value_set_object( out, MANAGEDGOBJECT( PEGETMANAGED( in ) )->object ); } else { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); error_top( _( "Unimplemented argument type." ) ); (void) itext_value( rc, &buf, in ); error_sub( _( "Cannot convert %s to GValue." ), vips_buf_all( &buf ) ); return( FALSE ); } return( TRUE ); } /* Try to make a heap object from a gvalue. */ gboolean heap_gvalue_to_ip( GValue *in, PElement *out ) { Reduce *rc = reduce_context; Heap *heap = rc->heap; if( G_VALUE_HOLDS_BOOLEAN( in ) ) { PEPUTP( out, ELEMENT_BOOL, (int) g_value_get_boolean( in ) ); } else if( G_VALUE_HOLDS_CHAR( in ) ) { /* g_value_get_schar() is not in older glibs. */ PEPUTP( out, ELEMENT_CHAR, (int) g_value_get_uchar( in ) ); } else if( G_VALUE_HOLDS_UCHAR( in ) ) { PEPUTP( out, ELEMENT_CHAR, (int) g_value_get_uchar( in ) ); } else if( G_VALUE_HOLDS_INT( in ) ) { if( !heap_real_new( heap, g_value_get_int( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_UINT( in ) ) { if( !heap_real_new( heap, g_value_get_uint( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_LONG( in ) ) { if( !heap_real_new( heap, g_value_get_long( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_ULONG( in ) ) { if( !heap_real_new( heap, g_value_get_ulong( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_INT64( in ) ) { if( !heap_real_new( heap, g_value_get_int64( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_UINT64( in ) ) { if( !heap_real_new( heap, g_value_get_uint64( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_FLOAT( in ) ) { if( !heap_real_new( heap, g_value_get_float( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_DOUBLE( in ) ) { if( !heap_real_new( heap, g_value_get_double( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_ENUM( in ) ) { if( !heap_real_new( heap, g_value_get_enum( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_STRING( in ) ) { if( !heap_managedstring_new( heap, g_value_get_string( in ), out ) ) return( FALSE ); } else if( G_VALUE_HOLDS_OBJECT( in ) ) { GObject *object; Managed *managed; object = g_value_get_object( in ); if( VIPS_IS_IMAGE( object ) ) { VipsImage *image = VIPS_IMAGE( object ); g_object_ref( image ); managed = MANAGED( imageinfo_new( main_imageinfogroup, heap, image, image->filename ) ); } else managed = MANAGED( managedgobject_new( heap, object ) ); PEPUTP( out, ELEMENT_MANAGED, managed ); } else if( g_value_type_transformable( G_VALUE_TYPE( in ), G_TYPE_STRING ) ) { GValue temp = { 0 }; g_value_init( &temp, G_TYPE_STRING ); g_value_transform( in, &temp ); if( !heap_managedstring_new( heap, g_value_get_string( &temp ), out ) ) { return( FALSE ); g_value_unset( &temp ); } g_value_unset( &temp ); } else { error_top( _( "Unimplemented type." ) ); error_sub( _( "Unable to convert %s to a nip type." ), G_VALUE_TYPE_NAME( in ) ); return( FALSE ); } return( TRUE ); } /* Indent step. */ #define TAB (2) /* Fwd ref. */ static void lisp_pelement( VipsBuf *buf, PElement *base, GSList **back, gboolean fn, int indent ); /* Print a sym-value list. */ static void lisp_symval( VipsBuf *buf, PElement *base, GSList **back, gboolean fn, int indent, PElement *stop ) { gboolean error = FALSE; /* Reached the "stop" element? */ if( stop && *base->type == *stop->type && *base->ele == *stop->ele ) return; if( PEISNODE( base ) ) { HeapNode *hn = PEGETVAL( base ); PElement pe; if( hn->type != TAG_CONS ) error = TRUE; PEPOINTLEFT( hn, &pe ); if( !error && PEISNODE( &pe ) ) { HeapNode *hn2 = PEGETVAL( &pe ); if( hn2->type != TAG_CONS ) error = TRUE; PEPOINTLEFT( hn2, &pe ); if( !error && PEISSYMREF( &pe ) ) { vips_buf_appendf( buf, "\n%s", spc( indent ) ); symbol_qualified_name( PEGETSYMREF( &pe ), buf ); vips_buf_appendf( buf, " = " ); PEPOINTRIGHT( hn2, &pe ); lisp_pelement( buf, &pe, back, fn, indent + TAB ); PEPOINTRIGHT( hn, &pe ); lisp_symval( buf, &pe, back, fn, indent, stop ); } else error = TRUE; } else error = TRUE; } else if( !PEISELIST( base ) ) error = TRUE; if( error ) vips_buf_appendf( buf, "\n%s<*** malformed symval list>", spc( indent ) ); } /* Print a [*] ... our caller has printed the enclosing [ ] and the first * element, so we print a ", " followed by us. */ static void lisp_list( VipsBuf *buf, PElement *base, GSList **back, gboolean fn, int indent ) { if( PEISNODE( base ) ) { HeapNode *hn = PEGETVAL( base ); PElement pe; vips_buf_appends( buf, ", " ); if( hn->type == TAG_CONS ) { PEPOINTLEFT( hn, &pe ); lisp_pelement( buf, &pe, back, fn, indent ); PEPOINTRIGHT( hn, &pe ); lisp_list( buf, &pe, back, fn, indent ); } else lisp_pelement( buf, base, back, fn, indent ); } else if( PEISMANAGEDSTRING( base ) ) { vips_buf_appends( buf, ", Managedstring <" ); vips_buf_appends( buf, PEGETMANAGEDSTRING( base )->string ); vips_buf_appends( buf, ">" ); } else if( !PEISELIST( base ) ) lisp_pelement( buf, base, back, fn, indent ); } /* Print a [char] ... fall back to lisp_list() if we hit a non-char * element. base is the RHS of a cons, so it can be a managedstring too. */ static gboolean lisp_string( VipsBuf *buf, PElement *base, GSList **back, gboolean fn, int indent ) { gboolean error = FALSE; if( PEISNODE( base ) ) { HeapNode *hn = PEGETVAL( base ); PElement pe; if( hn->type != TAG_CONS ) error = TRUE; PEPOINTLEFT( hn, &pe ); if( !error ) { if( PEISCHAR( &pe ) ) { vips_buf_appendf( buf, "%c", PEGETCHAR( &pe ) ); PEPOINTRIGHT( hn, &pe ); (void) lisp_string( buf, &pe, back, fn, indent ); } else { vips_buf_appends( buf, "\":[" ); lisp_pelement( buf, &pe, back, fn, indent ); PEPOINTRIGHT( hn, &pe ); lisp_list( buf, &pe, back, fn, indent ); vips_buf_appends( buf, "]" ); error = TRUE; } } else error = TRUE; } else if( PEISMANAGEDSTRING( base ) ) vips_buf_appends( buf, PEGETMANAGEDSTRING( base )->string ); else if( !PEISELIST( base ) ) error = TRUE; return( error ); } /* Print a graph LISP-style. */ static void lisp_node( VipsBuf *buf, HeapNode *hn, GSList **back, gboolean fn, int indent ) { int i; PElement p1, p2; /* Have we printed this node before? */ if( hn->flgs & FLAG_PRINT ) { if( (i = g_slist_index( *back, hn )) == -1 ) { *back = g_slist_prepend( *back, hn ); vips_buf_appendf( buf, "<" ); vips_buf_appendf( buf, _( "circular" ) ); vips_buf_appendf( buf, " (%p)>", hn ); } else { vips_buf_appendf( buf, "<" ); vips_buf_appendf( buf, _( "circular to label %d" ), i ); vips_buf_appendf( buf, ">" ); } return; } hn->flgs |= FLAG_PRINT; if( (i = g_slist_index( *back, hn )) != -1 ) { vips_buf_appendf( buf, "*" ); vips_buf_appendf( buf, _( "label %d" ), i ); vips_buf_appendf( buf, ": " ); } switch( hn->type ) { case TAG_APPL: if( fn ) { PEPOINTLEFT( hn, &p1 ); PEPOINTRIGHT( hn, &p2 ); vips_buf_appends( buf, "(" ); lisp_pelement( buf, &p1, back, fn, indent ); vips_buf_appends( buf, " " ); lisp_pelement( buf, &p2, back, fn, indent ); vips_buf_appends( buf, ")" ); } else { vips_buf_appends( buf, "<" ); vips_buf_appends( buf, _( "unevaluated" ) ); vips_buf_appends( buf, ">" ); } break; case TAG_CONS: PEPOINTLEFT( hn, &p1 ); if( PEISCHAR( &p1 ) ) { vips_buf_appendf( buf, "\"%c", PEGETCHAR( &p1 ) ); PEPOINTRIGHT( hn, &p2 ); (void) lisp_string( buf, &p2, back, fn, indent ); vips_buf_appends( buf, "\"" ); } else { vips_buf_appends( buf, "[" ); lisp_pelement( buf, &p1, back, fn, indent ); PEPOINTRIGHT( hn, &p2 ); lisp_list( buf, &p2, back, fn, indent ); vips_buf_appends( buf, "]" ); } break; case TAG_DOUBLE: vips_buf_appendf( buf, "%g", hn->body.num ); break; case TAG_COMPLEX: vips_buf_appendf( buf, "(%g,%g)", GETLEFT( hn )->body.num, GETRIGHT( hn )->body.num ); break; case TAG_CLASS: if( fn ) { vips_buf_appendf( buf, "\n%s", spc( indent ) ); vips_buf_appendf( buf, _( "class (%p)" ), hn ); vips_buf_appendf( buf, " " ); } PEPOINTLEFT( hn, &p1 ); lisp_pelement( buf, &p1, back, fn, indent ); if( fn ) { hn = GETRIGHT( hn ); vips_buf_appendf( buf, "\n%s", spc( indent + TAB ) ); vips_buf_appendf( buf, _( "members" ) ); vips_buf_appendf( buf, " = { " ); PEPOINTRIGHT( hn, &p1 ); lisp_symval( buf, &p1, back, fn, indent + TAB * 2, NULL ); vips_buf_appendf( buf, "\n%s}", spc( indent + TAB ) ); PEPOINTLEFT( hn, &p2 ); if( *p1.type != *p2.type || *p1.ele != *p2.ele ) { vips_buf_appendf( buf, "\n%s", spc( indent + TAB ) ); vips_buf_appendf( buf, _( "secret" ) ); vips_buf_appendf( buf, " = { " ); lisp_symval( buf, &p2, back, fn, indent + TAB * 2, &p1 ); vips_buf_appendf( buf, "\n%s} ", spc( indent + TAB ) ); } } break; case TAG_GEN: vips_buf_appendf( buf, "[%g,%g...", GETLEFT( hn )->body.num, GETLEFT( GETRIGHT( hn ) )->body.num ); if( GETRT( GETRIGHT( hn ) ) == ELEMENT_ELIST ) vips_buf_appends( buf, "[ ]]" ); else vips_buf_appendf( buf, "%g]", GETRIGHT( GETRIGHT( hn ) )->body.num ); break; case TAG_SHARED: PEPOINTLEFT( hn, &p1 ); i = GPOINTER_TO_INT( GETRIGHT( hn ) ); vips_buf_appendf( buf, "SHARE%d[", i ); lisp_pelement( buf, &p1, back, fn, indent ); vips_buf_appends( buf, "]" ); break; case TAG_REFERENCE: i = GPOINTER_TO_INT( GETRIGHT( GETLEFT( hn ) ) ); vips_buf_appendf( buf, "REF%d", i ); break; case TAG_FREE: default: g_assert( FALSE ); } } /* Print a pelement LISP-style. */ static void lisp_pelement( VipsBuf *buf, PElement *base, GSList **back, gboolean fn, int indent ) { HeapNode *hn; EType type = PEGETTYPE( base ); switch( type ) { case ELEMENT_NOVAL: vips_buf_appends( buf, "<" ); vips_buf_appendf( buf, _( "no value (type %d)" ), GPOINTER_TO_INT( PEGETVAL( base ) ) ); vips_buf_appends( buf, ">" ); break; case ELEMENT_NODE: if( !(hn = PEGETVAL( base )) ) { vips_buf_appends( buf, "<" ); vips_buf_appends( buf, _( "NULL pointer" ) ); vips_buf_appends( buf, ">" ); } else lisp_node( buf, hn, back, fn, indent ); break; case ELEMENT_SYMBOL: vips_buf_appends( buf, "<" ); vips_buf_appends( buf, _( "symbol" ) ); vips_buf_appends( buf, " \"" ); symbol_qualified_name( PEGETSYMBOL( base ), buf ); vips_buf_appends( buf, "\">" ); break; case ELEMENT_CONSTRUCTOR: vips_buf_appends( buf, "<" ); vips_buf_appends( buf, _( "constructor" ) ); vips_buf_appends( buf, " \"" ); symbol_qualified_name( PEGETCOMPILE( base )->sym, buf ); vips_buf_appends( buf, "\">" ); break; case ELEMENT_SYMREF: vips_buf_appends( buf, "<" ); vips_buf_appends( buf, _( "symref" ) ); vips_buf_appends( buf, " \"" ); symbol_qualified_name( PEGETSYMBOL( base ), buf ); vips_buf_appends( buf, "\">" ); break; case ELEMENT_COMPILEREF: vips_buf_appends( buf, "<" ); vips_buf_appends( buf, _( "compileref" ) ); vips_buf_appends( buf, " \"" ); symbol_qualified_name( PEGETCOMPILE( base )->sym, buf ); vips_buf_appends( buf, "\">" ); break; case ELEMENT_CHAR: vips_buf_appendf( buf, "'%c'", (int) PEGETCHAR( base ) ); break; case ELEMENT_BOOL: vips_buf_appends( buf, bool_to_char( PEGETBOOL( base ) ) ); break; case ELEMENT_BINOP: vips_buf_appends( buf, decode_BinOp( PEGETBINOP( base ) ) ); break; case ELEMENT_UNOP: vips_buf_appends( buf, decode_UnOp( PEGETUNOP( base ) ) ); break; case ELEMENT_ELIST: vips_buf_appends( buf, "[ ]" ); break; case ELEMENT_TAG: vips_buf_appendf( buf, "<" ); vips_buf_appendf( buf, _( "tag \"%s\"" ), PEGETTAG( base ) ); vips_buf_appendf( buf, ">" ); break; case ELEMENT_MANAGED: vips_buf_appendf( buf, "", PEGETVAL( base ) ); break; case ELEMENT_COMB: vips_buf_appends( buf, decode_CombinatorType( PEGETCOMB( base ) ) ); break; default: vips_buf_appendf( buf, "<" ); vips_buf_appendf( buf, _( "unknown element tag %d" ), type ); vips_buf_appendf( buf, ">" ); break; } } /* Print a node to a buffer. If fn is true, trace into functions. */ void graph_node( Heap *heap, VipsBuf *buf, HeapNode *root, gboolean fn ) { GSList *back; char txt[4]; VipsBuf buf2 = VIPS_BUF_STATIC( txt ); /* May be called before heap is built. */ if( !heap ) return; back = NULL; heap_clear( heap, FLAG_PRINT ); lisp_node( &buf2, root, &back, fn, 0 ); heap_clear( heap, FLAG_PRINT ); lisp_node( buf, root, &back, fn, 0 ); IM_FREEF( g_slist_free, back ); } /* As above, but start from a pelement. */ void graph_pelement( Heap *heap, VipsBuf *buf, PElement *root, gboolean fn ) { GSList *back; char txt[4]; VipsBuf buf2 = VIPS_BUF_STATIC( txt ); /* May be called before heap is built. */ if( !heap ) return; /* We print twice ... the first time through we build the list of back * pointers so we can label the graph correctly. */ back = NULL; heap_clear( heap, FLAG_PRINT ); lisp_pelement( &buf2, root, &back, fn, 0 ); heap_clear( heap, FLAG_PRINT ); lisp_pelement( buf, root, &back, fn, 0 ); IM_FREEF( g_slist_free, back ); } /* As above, but start from an element. */ void graph_element( Heap *heap, VipsBuf *buf, Element *root, gboolean fn ) { PElement base; PEPOINTE( &base, root ); graph_pelement( heap, buf, &base, fn ); } void graph_pointer( PElement *root ) { char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( reduce_context->heap, &buf, root, TRUE ); printf( "%s\n", vips_buf_all( &buf ) ); } /* Fwd ref. */ static void shell_pelement( PElement *base ); /* Print a graph shell-style. */ static void shell_node( HeapNode *hn ) { PElement p1, p2; /* Have we printed this node before? */ if( hn->flgs & FLAG_PRINT ) { printf( "<*circular*>" ); return; } hn->flgs |= FLAG_PRINT; switch( hn->type ) { case TAG_CLASS: case TAG_APPL: case TAG_REFERENCE: case TAG_SHARED: case TAG_GEN: break; case TAG_CONS: { gboolean string_mode; PEPOINTLEFT( hn, &p1 ); string_mode = PEISCHAR( &p1 ); for(;;) { if( string_mode ) printf( "%c", PEGETCHAR( &p1 ) ); else shell_pelement( &p1 ); PEPOINTRIGHT( hn, &p2 ); if( PEISMANAGEDSTRING( &p2 ) ) { printf( "%s\n", PEGETMANAGEDSTRING( &p2 )->string ); break; } else if( PEISELIST( &p2 ) ) break; if( !string_mode ) printf( "\n" ); hn = PEGETVAL( &p2 ); PEPOINTLEFT( hn, &p1 ); if( string_mode && !PEISCHAR( &p1 ) ) string_mode = FALSE; } } break; case TAG_DOUBLE: printf( "%g", hn->body.num ); break; case TAG_COMPLEX: printf( "%g %g", GETLEFT( hn )->body.num, GETRIGHT( hn )->body.num ); break; case TAG_FREE: default: g_assert( FALSE ); } } /* Print a pelement shell-style. */ static void shell_pelement( PElement *base ) { switch( PEGETTYPE( base ) ) { /* Only allow concrete base types. */ case ELEMENT_SYMREF: case ELEMENT_COMPILEREF: case ELEMENT_CONSTRUCTOR: case ELEMENT_BINOP: case ELEMENT_UNOP: case ELEMENT_COMB: case ELEMENT_TAG: case ELEMENT_SYMBOL: case ELEMENT_NOVAL: printf( "no-value" ); break; case ELEMENT_NODE: shell_node( PEGETVAL( base ) ); break; case ELEMENT_CHAR: printf( "%c", (int)PEGETCHAR( base ) ); break; case ELEMENT_BOOL: printf( "%s", bool_to_char( PEGETBOOL( base ) ) ); break; case ELEMENT_ELIST: printf( "[ ]" ); break; case ELEMENT_MANAGED: if( PEISIMAGE( base ) ) printf( "%s", PEGETIMAGE( base )->filename ); else if( PEISMANAGEDSTRING( base ) ) printf( "%s", PEGETMANAGEDSTRING( base )->string ); break; default: g_assert( FALSE ); } } /* Print a pelement shell-style. */ void graph_value( PElement *root ) { Reduce *rc = reduce_context; if( !reduce_pelement( rc, reduce_spine_strict, root ) ) { iwindow_alert( NULL, GTK_MESSAGE_ERROR ); return; } heap_clear( reduce_context->heap, FLAG_PRINT ); shell_pelement( root ); printf( "\n" ); } ================================================ FILE: src/heap.h ================================================ /* Heap management. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Node type. Generally represent data objects. * * Don't use enum, as we want this to fit in 1 byte. */ typedef unsigned char NodeType; #define TAG_APPL (0) /* Application */ #define TAG_CONS (1) /* List cons */ #define TAG_FREE (2) /* On free list */ #define TAG_DOUBLE (3) /* Constant double */ #define TAG_COMPLEX (4) /* Constant complex */ #define TAG_GEN (5) /* List generator */ #define TAG_CLASS (8) /* Class object */ #define TAG_SHARED (9) /* Root of a common sub-expression */ #define TAG_REFERENCE (10) /* Reference to a common sub-expression */ #define TAG_FILE (12) /* Generate list from file */ /* Element types. Generally represent operators. */ typedef unsigned char EType; #define ELEMENT_NOVAL (0) /* No value */ #define ELEMENT_NODE (1) /* Pointer to another node */ #define ELEMENT_SYMBOL (2) /* Pointer to Symbol, reduces to value */ #define ELEMENT_SYMREF (3) /* Pointer to Symbol, does not reduce */ #define ELEMENT_COMPILEREF (4) /* Pointer to Compile, does not reduce */ #define ELEMENT_CHAR (5) /* Boxed char type */ #define ELEMENT_BOOL (6) /* Boxed bool type */ #define ELEMENT_BINOP (7) /* Binary operator */ #define ELEMENT_UNOP (8) /* Unary operator */ #define ELEMENT_COMB (9) /* Combinator */ #define ELEMENT_TAG (10) /* RHS of '.' operator */ #define ELEMENT_MANAGED (11) /* A managed object */ #define ELEMENT_CONSTRUCTOR (12)/* Class constructor */ #define ELEMENT_ELIST (13) /* Empty list */ /* Flags we attach to a node. */ typedef unsigned char NodeFlags; #define FLAG_SERIAL (31) /* Serial number mask .. must be 1st */ #define FLAG_PRINT (32) /* Marked (for decompile print) */ #define FLAG_DEBUG (64) /* Marked (for debug traverse) */ #define FLAG_MARK (128) /* Marked (for mark-sweep) */ #define FLAG_ALL (255) /* All flags mask */ /* Set the serial number without disturbing other stuff. */ #define SETSERIAL( FLAGS, SERIAL ) { \ (FLAGS) = ((FLAGS) & (FLAG_SERIAL ^ FLAG_ALL)) | \ ((SERIAL) & FLAG_SERIAL); \ } /* Combinators. Don't change the order of these! See reduce.c for an array * indexed with a CombinatorType. */ typedef enum combinator_type { COMB_S = 0, /* S combinator */ COMB_SL, /* S-left combinator */ COMB_SR, /* S-right combinator */ COMB_I, /* Identity combinator */ COMB_K, /* K combinator */ COMB_GEN /* List generator combinator */ } CombinatorType; /* An element ... a tag plus a pointer. Use one of these to hold a pointer * into a heap. */ typedef struct _Element { EType type; void *ele; } Element; /* A node on the heap. Should fit in 12 bytes on most machines. */ typedef struct _HeapNode { /* Elements: either a pair of pointers, or a double. Sensible on most * 32-bit systems, not so great on 64 bitters. */ union { struct { void *left; void *right; } ptrs; double num; } body; /* Flags ... should fit in 4 bytes. */ NodeType type; /* What this is */ NodeFlags flgs; /* GC flags etc */ EType ltype; /* Type of left element */ EType rtype; /* Type of right element */ } HeapNode; /* Put type/value pairs into nodes. Make sure we completely read before we * write. */ #define PPUTLEFT(N,T,V) {\ EType t99 = (T);\ void *v99 = (void*)(V);\ \ (N)->ltype = t99;\ (N)->body.ptrs.left = v99;\ } #define PPUTRIGHT(N,T,V) {\ EType t99 = (T);\ void *v99 = (void*)(V);\ \ (N)->rtype = t99;\ (N)->body.ptrs.right = v99;\ } #define PPUT(N,Tl,Vl,Tr,Vr) {PPUTLEFT( N, Tl, Vl ); PPUTRIGHT( N, Tr, Vr );} /* Get value as a HeapNode pointer (most common case). */ #define GETLEFT(N) ((HeapNode*)((N)->body.ptrs.left)) #define GETRIGHT(N) ((HeapNode*)((N)->body.ptrs.right)) #define GETLT(N) ((N)->ltype) #define GETRT(N) ((N)->rtype) /* A pointer to an element inside a HeapNode, or to an Element. */ typedef struct pelement { EType *type; void **ele; } PElement; /* Make a PElement point to a node. */ #define PEPOINTLEFT(N,P) \ {(P)->type=&((N)->ltype);(P)->ele=&((N)->body.ptrs.left);} #define PEPOINTRIGHT(N,P) \ {(P)->type=&((N)->rtype);(P)->ele=&((N)->body.ptrs.right);} /* Make a PElement point to an element. */ #define PEPOINTE(PE,E) \ {(PE)->type=&((E)->type);(PE)->ele=&((E)->ele);} /* Get from a PE. */ #define PEGETTYPE(P) (*((P)->type)) #define PEGETVAL(P) ((HeapNode*)(*((P)->ele))) #define PEGETE(P,E) ((E)->type = PEGETTYPE(P),(E)->ele = PEGETVAL(P)) #define PEGETP(PE,T,V) ((T)=*((PE)->type),(V)=*((PE)->ele)) /* Write to a PE. We are careful to eval all args before writing, in case we * are writing to one of the inputs. */ #define PEPUTE(PE,E) {*((PE)->type)=(E)->type;*((PE)->ele)=(E)->ele;} #define PEPUTPE(PEto,PEfrom) {\ EType t99 = PEGETTYPE(PEfrom);\ void *v99 = PEGETVAL(PEfrom);\ \ *((PEto)->type) = t99;\ *((PEto)->ele) = v99;\ } #define PEPUTP(PE,T,V) {\ EType t99 = (T);\ void *v99 = GUINT_TO_POINTER(V);\ \ *((PE)->type) = t99;\ *((PE)->ele) = v99;\ } /* Write a PE to a node. Again, make sure we read both before we write, in * case we are writing an expression to ourselves. */ #define PEPUTLEFT(N,PE) {\ EType t99 = PEGETTYPE(PE);\ void *v99 = PEGETVAL(PE);\ \ (N)->ltype = t99;\ (N)->body.ptrs.left = v99;\ } #define PEPUTRIGHT(N,PE) {\ EType t99 = PEGETTYPE(PE);\ void *v99 = PEGETVAL(PE);\ \ (N)->rtype = t99;\ (N)->body.ptrs.right = v99;\ } /* Predicates. */ #define PEISBINOP(P) (PEGETTYPE(P) == ELEMENT_BINOP) #define PEISBOOL(P) (PEGETTYPE(P) == ELEMENT_BOOL) #define PEISCHAR(P) (PEGETTYPE(P) == ELEMENT_CHAR) #define PEISCLASS(P) (PEISNODE(P) && PEGETVAL(P)->type == TAG_CLASS) #define PEISCONSTRUCTOR(P) (PEGETTYPE(P) == ELEMENT_CONSTRUCTOR) #define PEISCOMB(P) (PEGETTYPE(P) == ELEMENT_COMB) #define PEISCOMPLEX(P) (PEISNODE(P) && PEGETVAL(P)->type == TAG_COMPLEX) #define PEISTAG(P) (PEGETTYPE(P) == ELEMENT_TAG) #define PEISMANAGED(P) (PEGETTYPE(P) == ELEMENT_MANAGED) #define PEISMANAGEDGOBJECT(P) (PEISMANAGED(P) && \ IS_MANAGEDGOBJECT( PEGETVAL(P) )) #define PEISMANAGEDSTRING(P) (PEISMANAGED(P) && \ IS_MANAGEDSTRING(PEGETVAL(P))) #define PEISIMAGE(P) (PEISMANAGED(P) && IS_IMAGEINFO( PEGETVAL(P) )) #define PEISVIPSOBJECT(P) \ (PEISMANAGEDGOBJECT(P) && VIPS_IS_OBJECT( PEGETMANAGEDGOBJECT(P) )) #define PEISFILE(P) (PEISMANAGED(P) && IS_MANAGEDFILE(PEGETVAL(P))) #define PEISELIST(P) (PEGETTYPE(P) == ELEMENT_ELIST) #define PEISFLIST(P) ((PEISNODE(P) && PEGETVAL(P)->type == TAG_CONS) || \ PEISMANAGEDSTRING(P)) #define PEISLIST(P) (PEISELIST(P) || PEISFLIST(P)) #define PEISNOVAL(P) (PEGETTYPE(P) == ELEMENT_NOVAL) #define PEISNUM(P) (PEISREAL(P) || PEISCOMPLEX(P)) #define PEISNODE(P) (PEGETTYPE(P) == ELEMENT_NODE) #define PEISREAL(P) (PEISNODE(P) && PEGETVAL(P)->type == TAG_DOUBLE) #define PEISSYMBOL(P) (PEGETTYPE(P) == ELEMENT_SYMBOL) #define PEISSYMREF(P) (PEGETTYPE(P) == ELEMENT_SYMREF) #define PEISCOMPILEREF(P) (PEGETTYPE(P) == ELEMENT_COMPILEREF) #define PEISUNOP(P) (PEGETTYPE(P) == ELEMENT_UNOP) /* Extract bits of primitive compound types. */ #define PEGETSYMBOL(P) ((Symbol*)PEGETVAL(P)) #define PEGETSYMREF(P) ((Symbol*)PEGETVAL(P)) #define PEGETCOMPILE(P) ((Compile*)(PEGETVAL(P))) #define PEGETBINOP(P) ((BinOp)PEGETVAL(P)) #define PEGETUNOP(P) ((UnOp)PEGETVAL(P)) #define PEGETCOMB(P) ((CombinatorType)PEGETVAL(P)) #define PEGETTAG(P) ((char*)PEGETVAL(P)) #define PEGETREAL(P) (PEGETVAL(P)->body.num) #define PEGETBOOL(P) ((gboolean)GPOINTER_TO_UINT(PEGETVAL(P))) #define PEGETCHAR(P) ((unsigned char)(GPOINTER_TO_UINT(PEGETVAL(P)))) #define PEGETIMAGE(P) (((Imageinfo*)PEGETVAL(P))->im) #define PEGETII(P) ((Imageinfo*)PEGETVAL(P)) #define PEGETFILE(P) ((Managedfile*)PEGETVAL(P)) #define PEGETMANAGED(P) ((Managed*)PEGETVAL(P)) #define PEGETMANAGEDSTRING(P) ((Managedstring*)PEGETVAL(P)) #define PEGETMANAGEDGOBJECT(P) (((Managedgobject*)PEGETVAL(P))->object) #define PEGETVIPSOBJECT(P) \ ((VipsObject*)(((Managedgobject*)PEGETVAL(P))->object)) #define PEGETHD(P1,P2) PEPOINTLEFT(PEGETVAL(P2), P1) #define PEGETTL(P1,P2) PEPOINTRIGHT(PEGETVAL(P2), P1) #define PEGETREALPART(P) (GETLEFT(PEGETVAL(P))->body.num) #define PEGETIMAGPART(P) (GETRIGHT(PEGETVAL(P))->body.num) #define PEGETCLASSCOMPILE(P) (COMPILE(GETLEFT(PEGETVAL(P)))) #define PEGETCLASSSECRET(P1,P2) PEPOINTLEFT(GETRIGHT(PEGETVAL(P2)),P1) #define PEGETCLASSMEMBER(P1,P2) PEPOINTRIGHT(GETRIGHT(PEGETVAL(P2)),P1) /* A block on the heap. */ struct _HeapBlock { Heap *heap; /* Heap we are part of */ HeapBlock *next; /* Next block in chain */ HeapNode *node; /* Nodes on this block */ int sz; /* Number of nodes in this block */ }; /* Function to get max heap size. */ typedef int (*heap_max_fn)( Heap * ); #define TYPE_HEAP (heap_get_type()) #define HEAP( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_HEAP, Heap )) #define HEAP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_HEAP, HeapClass)) #define IS_HEAP( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_HEAP )) #define IS_HEAP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_HEAP )) #define HEAP_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_HEAP, HeapClass )) struct _Heap { iObject parent_object; Compile *compile; /* If non-null, assoc. compile */ heap_max_fn max_fn; /* Max nodes in this heap */ int mxb; /* Max blocks until next check */ int rsz; /* Nodes to allocate in each extra block */ int nb; /* Number of blocks attached */ HeapBlock *hb; /* List of current blocks */ HeapNode *free; /* Start of free-node chain (sweep to here) */ int ncells; /* Cells allocated */ int nfree; /* Cells free */ int serial; /* Last serial number we used */ gboolean filled; /* Set on heap full */ GHashTable *emark; /* Set of elements to mark on GC */ GHashTable *rmark; /* Set of Reduce to mark on GC */ GHashTable *mtable; /* Managed associated with this heap */ guint gc_tid; /* id of gc delay timer */ /* Set this to force unreffed objects out immediately. Handy for leak * testing. */ gboolean flush; }; typedef struct _HeapClass { iObjectClass parent_class; /* My methods. */ } HeapClass; /* Get a node from the free-list. No check for free-list exhausted! Set sym * pointer in node to heap sym pointer. */ #ifdef DEBUG_HEAP #define EXTRACTNODE( H, A ) \ (heap_sanity( H ), (A) = (H)->free, (H)->free = GETLEFT( A ), 0) #else /*!DEBUG_HEAP*/ #define EXTRACTNODE( H, A ) \ ((A) = (H)->free, (H)->free = GETLEFT( A ), 0) #endif /*DEBUG_HEAP*/ /* Allocate a new node from heap H, pop the pointer into A, return non-zero if * alloc failed. Node is uninitialised! */ #define NEWNODE( H, A ) ( \ (H)->free ? \ EXTRACTNODE( H, A ): \ (((A) = heap_getmem( H )) ? 0 : -1) \ ) typedef void *(*heap_safe_pointer_fn)( Heap *heap, PElement *, void *, void *, void *, void * ); void *heap_safe_pointer( Heap *heap, heap_safe_pointer_fn fn, void *a, void *b, void *c, void *d ); typedef void *(*heap_map_fn)( HeapNode *, void *, void *); void *heap_map( HeapNode *hn, heap_map_fn fn, void *a, void *b ); int heap_sanity( Heap *heap ); void heap_check_all_destroyed( void ); void heap_destroy( Heap *heap ); GType heap_get_type( void ); Heap *heap_new( Compile *compile, heap_max_fn max_fn, int stsz, int rsz ); HeapNode *heap_getmem( Heap *heap ); gboolean heap_gc( Heap *heap ); void heap_gc_request( Heap *heap ); void heap_register_element( Heap *heap, Element *root ); void heap_unregister_element( Heap *heap, Element *root ); void heap_register_reduce( Heap *heap, Reduce *rc ); void heap_unregister_reduce( Heap *heap, Reduce *rc ); void heap_set( Heap *heap, NodeFlags setmask ); void heap_clear( Heap *heap, NodeFlags clearmask ); int heap_serial_new( Heap *heap ); gboolean heap_bool_new( Heap *heap, gboolean val, PElement *out ); gboolean heap_real_new( Heap *heap, double in, PElement *out ); gboolean heap_element_new( Heap *heap, Element *e, PElement *out ); gboolean heap_complex_element_new( Heap *heap, PElement *rp, PElement *ip, PElement *out ); gboolean heap_complex_new( Heap *heap, double rp, double ip, PElement *out ); gboolean heap_realvec_new( Heap *heap, int n, double *vec, PElement *out ); gboolean heap_intvec_new( Heap *heap, int n, int *vec, PElement *out ); void heap_list_init( PElement *list ); gboolean heap_list_add( Heap *heap, PElement *list, PElement *data ); gboolean heap_list_next( PElement *list ); gboolean heap_list_cat( Reduce *rc, PElement *a, PElement *b, PElement *out ); void heap_appl_init( PElement *base, PElement *func ); gboolean heap_appl_add( Heap *heap, PElement *base, PElement *parm ); gboolean heap_matrix_new( Heap *heap, int xsize, int ysize, double *vec, PElement *out ); gboolean heap_string_new( Heap *heap, const char *str, PElement *out ); gboolean heap_managedstring_new( Heap *heap, const char *str, PElement *out ); gboolean heap_lstring_new( Heap *heap, GSList *labels, PElement *out ); gboolean heap_file_new( Heap *heap, const char *filename, PElement *out ); gboolean heap_error_typecheck( PElement *e, const char *name, const char *type ); typedef void *(*heap_map_list_fn)( PElement *, void *, void * ); void *heap_map_list( PElement *base, heap_map_list_fn fn, void *a, void *b ); typedef void *(*heap_map_dict_fn)( const char *, PElement *, void *a, void *b ); void *heap_map_dict( PElement *base, heap_map_dict_fn fn, void *a, void *b ); gboolean heap_get_list( PElement *list ); gboolean heap_get_list_next( PElement *list, PElement *data ); gboolean heap_get_string( PElement *base, char *buf, int n ); gboolean heap_get_lstring( PElement *base, GSList **labels ); gboolean heap_get_bool( PElement *base, gboolean *out ); gboolean heap_get_real( PElement *base, double *out ); gboolean heap_get_class( PElement *base, PElement *out ); gboolean heap_get_image( PElement *base, Imageinfo **out ); int heap_get_realvec( PElement *base, double *buf, int n ); int heap_get_imagevec( PElement *base, Imageinfo **buf, int n ); gboolean heap_get_matrix_size( PElement *base, int *xsize, int *ysize ); gboolean heap_get_matrix( PElement *base, double *buf, int n, int *xsize, int *ysize ); gboolean heap_is_elist( PElement *base, gboolean *out ); gboolean heap_is_list( PElement *base, gboolean *out ); gboolean heap_is_string( PElement *base, gboolean *out ); gboolean heap_is_realvec( PElement *base, gboolean *out ); gboolean heap_is_imagevec( PElement *base, gboolean *out ); gboolean heap_is_matrix( PElement *base, gboolean *out ); gboolean heap_is_class( PElement *base, gboolean *out ); gboolean heap_is_instanceof_exact( const char *name, PElement *klass, gboolean *out); gboolean heap_is_instanceof( const char *name, PElement *klass, gboolean *out ); int heap_list_length( PElement *base ); int heap_list_length_max( PElement *base, int max_length ); gboolean heap_list_index( PElement *base, int n, PElement *out ); gboolean heap_reduce_strict( PElement *base ); gboolean heap_copy( Heap *heap, Compile *compile, PElement *out ); gboolean heap_ip_to_gvalue( PElement *in, GValue *out ); gboolean heap_gvalue_to_ip( GValue *in, PElement *out ); void graph_node( Heap *heap, VipsBuf *buf, HeapNode *root, gboolean fn ); void graph_pelement( Heap *heap, VipsBuf *buf, PElement *root, gboolean fn ); void graph_element( Heap *heap, VipsBuf *buf, Element *root, gboolean fn ); void graph_pointer( PElement *root ); /* Reduce and print, csh-style output. */ void graph_value( PElement *root ); ================================================ FILE: src/heapmodel.c ================================================ /* base class for models of heap classes */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ModelClass *parent_class = NULL; void * heapmodel_new_heap( Heapmodel *heapmodel, PElement *root ) { HeapmodelClass *heapmodel_class = HEAPMODEL_GET_CLASS( heapmodel ); if( heapmodel_class->new_heap ) { void *res; res = heapmodel_class->new_heap( heapmodel, root ); return( res ); } return( NULL ); } void * heapmodel_update_model( Heapmodel *heapmodel ) { HeapmodelClass *heapmodel_class = HEAPMODEL_GET_CLASS( heapmodel ); #ifdef DEBUG printf( "heapmodel_update_model: %s ", G_OBJECT_TYPE_NAME( heapmodel ) ); row_name_print( heapmodel->row ); printf( " modified = %d\n", heapmodel->modified ); #endif /*DEBUG*/ if( heapmodel_class->update_model && !heapmodel->modified ) { void *res; res = heapmodel_class->update_model( heapmodel ); return( res ); } return( NULL ); } void * heapmodel_update_heap( Heapmodel *heapmodel ) { HeapmodelClass *heapmodel_class = HEAPMODEL_GET_CLASS( heapmodel ); if( heapmodel_class->update_heap && heapmodel->modified ) { void *res; res = heapmodel_class->update_heap( heapmodel ); return( res ); } return( NULL ); } void * heapmodel_clear_edited( Heapmodel *heapmodel ) { HeapmodelClass *heapmodel_class = HEAPMODEL_GET_CLASS( heapmodel ); if( heapmodel_class->clear_edited ) return( heapmodel_class->clear_edited( heapmodel ) ); return( NULL ); } static Rhs * heapmodel_get_rhs( Heapmodel *heapmodel ) { iContainer *p; /* Search for the enclosing RHS ... may not be one if (eg.) this is a * top-level row. */ for( p = ICONTAINER( heapmodel )->parent; p; p = p->parent ) if( IS_RHS( p ) ) return( RHS( p ) ); return( NULL ); } static Row * heapmodel_get_row( Heapmodel *heapmodel ) { Rhs *rhs; if( IS_RHS( heapmodel ) ) return( ROW( ICONTAINER( heapmodel )->parent ) ); else if( (rhs = heapmodel_get_rhs( heapmodel )) ) return( HEAPMODEL( rhs )->row ); else return( NULL ); } static void heapmodel_parent_add( iContainer *child ) { Heapmodel *heapmodel = HEAPMODEL( child ); g_assert( IS_HEAPMODEL( child->parent ) || IS_FILEMODEL( child->parent ) ); ICONTAINER_CLASS( parent_class )->parent_add( child ); /* Update our context. */ heapmodel->rhs = heapmodel_get_rhs( heapmodel ); heapmodel->row = heapmodel_get_row( heapmodel ); } static void * heapmodel_real_new_heap( Heapmodel *heapmodel, PElement *root ) { iobject_changed( IOBJECT( heapmodel ) ); return( NULL ); } static void * heapmodel_real_update_model( Heapmodel *heapmodel ) { iobject_changed( IOBJECT( heapmodel ) ); return( NULL ); } static void * heapmodel_real_update_heap( Heapmodel *heapmodel ) { g_assert( heapmodel->modified ); heapmodel_set_modified( heapmodel, FALSE ); return( NULL ); } static void * heapmodel_real_clear_edited( Heapmodel *heapmodel ) { return( NULL ); } static void heapmodel_class_init( HeapmodelClass *class ) { HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; parent_class = g_type_class_peek_parent( class ); /* Init methods. */ icontainer_class->parent_add = heapmodel_parent_add; heapmodel_class->new_heap = heapmodel_real_new_heap; heapmodel_class->update_heap = heapmodel_real_update_heap; heapmodel_class->update_model = heapmodel_real_update_model; heapmodel_class->clear_edited = heapmodel_real_clear_edited; } static void heapmodel_init( Heapmodel *heapmodel ) { heapmodel->row = NULL; heapmodel->rhs = NULL; heapmodel->modified = FALSE; } GType heapmodel_get_type( void ) { static GType heapmodel_type = 0; if( !heapmodel_type ) { static const GTypeInfo info = { sizeof( HeapmodelClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) heapmodel_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Heapmodel ), 32, /* n_preallocs */ (GInstanceInitFunc) heapmodel_init, }; heapmodel_type = g_type_register_static( TYPE_MODEL, "Heapmodel", &info, 0 ); } return( heapmodel_type ); } void heapmodel_set_modified( Heapmodel *heapmodel, gboolean modified ) { if( heapmodel->modified != modified ) { #ifdef DEBUG { HeapmodelClass *heapmodel_class = HEAPMODEL_GET_CLASS( heapmodel ); printf( "heapmodel_set_modified: %s::", G_OBJECT_CLASS_NAME( heapmodel_class ) ); row_name_print( heapmodel->row ); printf( " %s\n", bool_to_char( modified ) ); } #endif /*DEBUG*/ heapmodel->modified = modified; iobject_changed( IOBJECT( heapmodel ) ); } } /* Generate a descriptive name for a heapmodel. Used for captions. */ gboolean heapmodel_name( Heapmodel *heapmodel, VipsBuf *buf ) { Row *row = heapmodel->row; Expr *expr; Symbol *sym; Toolitem *toolitem; if( !row || !(expr = row->expr) || !PEISCLASS( &expr->root ) ) return( FALSE ); sym = PEGETCLASSCOMPILE( &expr->root )->sym; /* If this is an action member we should be able to look up * it's sym and get a descriptive label. */ if( (toolitem = toolitem_lookup( row->ws->kitg, sym )) ) vips_buf_appends( buf, toolitem->name ); else symbol_qualified_name_relative( row->ws->sym, sym, buf ); return( TRUE ); } /* Print the value member to a buf. */ gboolean heapmodel_value( Heapmodel *heapmodel, VipsBuf *buf ) { Expr *expr; PElement value; if( !heapmodel->row || !(expr = heapmodel->row->expr) || expr->err || expr->sym->dirty || !class_get_member( &expr->root, MEMBER_VALUE, NULL, &value ) ) return( FALSE ); itext_value( reduce_context, buf, &value ); return( TRUE ); } ================================================ FILE: src/heapmodel.h ================================================ /* like a model, but something that represents a part of the heap (eg. * toggle/slider/text etc.) */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_HEAPMODEL (heapmodel_get_type()) #define HEAPMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_HEAPMODEL, Heapmodel )) #define HEAPMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_HEAPMODEL, HeapmodelClass)) #define IS_HEAPMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_HEAPMODEL )) #define IS_HEAPMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_HEAPMODEL )) #define HEAPMODEL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_HEAPMODEL, HeapmodelClass )) struct _Heapmodel { Model parent_class; /* Context. */ Row *row; /* Enclosing row */ Rhs *rhs; /* Enclosing rhs */ /* Set if model has changes which have not yet been applied to the * heap ... update_model() blocks, update_heap() clears. */ gboolean modified; }; typedef struct _HeapmodelClass { ModelClass parent_class; /* Building heaps from models, building models from heaps. new_heap the heap has changed ... recurse down adding, updating (with a recursive new_heap()) and removing children update_model read the heap into the model ... eg. update text representation update_heap if the heapmodel has any unapplied user edits, use them to update the heap ... update the heap area pointed to by the last update_model clear_edited set back to default values */ void *(*new_heap)( Heapmodel *, PElement * ); void *(*update_model)( Heapmodel * ); void *(*update_heap)( Heapmodel * ); void *(*clear_edited)( Heapmodel * ); } HeapmodelClass; void *heapmodel_new_heap( Heapmodel *heapmodel, PElement *root ); void *heapmodel_update_model( Heapmodel *heapmodel ); void *heapmodel_update_heap( Heapmodel *heapmodel ); void *heapmodel_clear_edited( Heapmodel *heapmodel ); GType heapmodel_get_type( void ); void heapmodel_set_modified( Heapmodel *heapmodel, gboolean modified ); gboolean heapmodel_name( Heapmodel *heapmodel, VipsBuf *buf ); gboolean heapmodel_value( Heapmodel *heapmodel, VipsBuf *buf ); ================================================ FILE: src/helpindex.h ================================================ { "sec:object", "nipguidese34.html#nip_label_sec:object" }, { "sec:menu-colour", "nipguidese14.html#nip_label_sec:menu-colour" }, { "tb:colour", "nipguidese14.html#nip_label_tb:colour" }, { "sec:pattern", "nipguidese30.html#nip_label_sec:pattern" }, { "sec:nerdtour", "nipguidese3.html#nip_label_sec:nerdtour" }, { "fg:Fred", "nipguidese3.html#nip_label_fg:Fred" }, { "fg:mainFred", "nipguidese3.html#nip_label_fg:mainFred" }, { "fg:slideFred", "nipguidese3.html#nip_label_fg:slideFred" }, { "fg:Jim", "nipguidese3.html#nip_label_fg:Jim" }, { "fg:twomoreregions", "nipguidese3.html#nip_label_fg:twomoreregions" }, { "fg:myjoin", "nipguidese3.html#nip_label_fg:myjoin" }, { "sec:menu-object", "nipguidese20.html#nip_label_sec:menu-object" }, { "sec:operators", "nipguidese27.html#nip_label_sec:operators" }, { "tb:precedence", "nipguidese27.html#nip_label_tb:precedence" }, { "sec:listsyntax", "nipguidese27.html#nip_label_sec:listsyntax" }, { "sec:func", "nipguidese27.html#nip_label_sec:func" }, { "sec:vidpref", "nipguidese4.html#nip_label_sec:vidpref" }, { "fg:vidpref", "nipguidese4.html#nip_label_fg:vidpref" }, { "sec:imcap", "nipguidese4.html#nip_label_sec:imcap" }, { "sec:grey", "nipguidese4.html#nip_label_sec:grey" }, { "sec:linuxgrey", "nipguidese4.html#nip_label_sec:linuxgrey" }, { "sec:wingrey", "nipguidese4.html#nip_label_sec:wingrey" }, { "sec:lists", "nipguidese28.html#nip_label_sec:lists" }, { "tb:list", "nipguidese28.html#nip_label_tb:list" }, { "sec:cmdline", "nipguidese13.html#nip_label_sec:cmdline" }, { "sec:menu-histogram", "nipguidese16.html#nip_label_sec:menu-histogram" }, { "sec:menu-matrix", "nipguidese19.html#nip_label_sec:menu-matrix" }, { "sec:quicktour", "nipguidese1.html#nip_label_sec:quicktour" }, { "fg:loadedimage", "nipguidese1.html#nip_label_fg:loadedimage" }, { "fg:imageview", "nipguidese1.html#nip_label_fg:imageview" }, { "tb:shortcuts", "nipguidese1.html#nip_label_tb:shortcuts" }, { "fg:imageviewregion", "nipguidese1.html#nip_label_fg:imageviewregion" }, { "fg:main2regions", "nipguidese1.html#nip_label_fg:main2regions" }, { "fg:rotate", "nipguidese1.html#nip_label_fg:rotate" }, { "fg:join", "nipguidese1.html#nip_label_fg:join" }, { "fg:browse", "nipguidese1.html#nip_label_fg:browse" }, { "tb:builtin", "nipguidese24.html#nip_label_tb:builtin" }, { "tb:toolkits", "nipguidese31.html#nip_label_tb:toolkits" }, { "sec:menu-filter", "nipguidese15.html#nip_label_sec:menu-filter" }, { "sec:progwin", "nipguidese12.html#nip_label_sec:progwin" }, { "sec:trace", "nipguidese12.html#nip_label_sec:trace" }, { "sec:menu-tasks", "nipguidese21.html#nip_label_sec:menu-tasks" }, { "sec:menu-capture", "nipguidese21.html#nip_label_sec:menu-capture" }, { "sec:menu-mosaic", "nipguidese21.html#nip_label_sec:menu-mosaic" }, { "sec:menu-picture-frame", "nipguidese21.html#nip_label_sec:menu-picture-frame" }, { "sec:menu-print", "nipguidese21.html#nip_label_sec:menu-print" }, { "sec:bowser", "nipguidese33.html#nip_label_sec:bowser" }, { "sec:tools", "nipguidese33.html#nip_label_sec:tools" }, { "fg:toolkit", "nipguidese33.html#nip_label_fg:toolkit" }, { "fg:toolkit2", "nipguidese33.html#nip_label_fg:toolkit2" }, { "fg:toolkit3", "nipguidese33.html#nip_label_fg:toolkit3" }, { "sec:workspaces", "nipguidese33.html#nip_label_sec:workspaces" }, { "fg:row2", "nipguidese33.html#nip_label_fg:row2" }, { "tb:classes", "nipguidese33.html#nip_label_tb:classes" }, { "sec:Image", "nipguidese33.html#nip_label_sec:Image" }, { "sec:colour", "nipguidese33.html#nip_label_sec:colour" }, { "sec:loadsave", "nipguidese10.html#nip_label_sec:loadsave" }, { "fg:open", "nipguidese10.html#nip_label_fg:open" }, { "fg:save", "nipguidese10.html#nip_label_fg:save" }, { "sec:view", "nipguidese9.html#nip_label_sec:view" }, { "fg:scr3", "nipguidese9.html#nip_label_fg:scr3" }, { "fg:scr4", "nipguidese9.html#nip_label_fg:scr4" }, { "sec:paintbox", "nipguidese9.html#nip_label_sec:paintbox" }, { "fg:paint", "nipguidese9.html#nip_label_fg:paint" }, { "sec:program", "nipguidech6.html#nip_label_sec:program" }, { "sec:lazy", "nipguidese29.html#nip_label_sec:lazy" }, { "sec:optimise", "nipguidese35.html#nip_label_sec:optimise" }, { "sec:menus", "nipguidech5.html#nip_label_sec:menus" }, { "sec:ipwindow", "nipguidese11.html#nip_label_sec:ipwindow" }, { "fg:startup", "nipguidese11.html#nip_label_fg:startup" }, { "sec:column", "nipguidese11.html#nip_label_sec:column" }, { "sec:row", "nipguidese11.html#nip_label_sec:row" }, { "fg:row", "nipguidese11.html#nip_label_fg:row" }, { "sec:apply", "nipguidese11.html#nip_label_sec:apply" }, { "sec:batch", "nipguidese11.html#nip_label_sec:batch" }, { "sec:error", "nipguidese11.html#nip_label_sec:error" }, { "sec:diaref", "nipguidese11.html#nip_label_sec:diaref" }, { "sec:menu-math", "nipguidese18.html#nip_label_sec:menu-math" }, { "sec:ir", "nipguidech3.html#nip_label_sec:ir" }, { "sec:mosaicing", "nipguidese5.html#nip_label_sec:mosaicing" }, { "sec:pieces", "nipguidese5.html#nip_label_sec:pieces" }, { "sec:tutorial", "nipguidech2.html#nip_label_sec:tutorial" }, { "sec:reference", "nipguidech4.html#nip_label_sec:reference" }, { "sec:class", "nipguidese32.html#nip_label_sec:class" }, { "sec:inheritance", "nipguidese32.html#nip_label_sec:inheritance" }, { "sec:balance", "nipguidese6.html#nip_label_sec:balance" }, { "sec:irtut", "nipguidese2.html#nip_label_sec:irtut" }, { "fg:loadsamples", "nipguidese2.html#nip_label_fg:loadsamples" }, { "fg:readyjoin", "nipguidese2.html#nip_label_fg:readyjoin" }, { "fg:joined", "nipguidese2.html#nip_label_fg:joined" }, { "sec:config", "nipguideap1.html#nip_label_sec:config" }, { "sec:menu-image", "nipguidese17.html#nip_label_sec:menu-image" }, { "sec:callvips", "nipguidese36.html#nip_label_sec:callvips" }, ================================================ FILE: src/iarrow.c ================================================ /* an ip arrow class object in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void iarrow_finalize( GObject *gobject ) { iArrow *iarrow; #ifdef DEBUG printf( "iarrow_finalize\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_IARROW( gobject ) ); iarrow = IARROW( gobject ); /* My instance finalize stuff. */ iregion_instance_destroy( &iarrow->instance ); vips_buf_destroy( &iarrow->caption_buffer ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void * iarrow_generate_caption_sub( iImage *iimage, iArrow *iarrow, gboolean *first ) { Workspace *ws = HEAPMODEL( iarrow )->row->ws; Row *row = HEAPMODEL( iimage )->row; /* Suppress this name in the caption if it's a superclass. If this * thing is on a super, it's on the subclass too ... not helpful to * have it twice. */ if( !is_super( row->sym ) ) { if( *first ) *first = FALSE; else vips_buf_appends( &iarrow->caption_buffer, ", " ); row_qualified_name_relative( ws->sym, row, &iarrow->caption_buffer ); } return( NULL ); } static const char * iarrow_generate_caption( iObject *iobject ) { static const char *names[] = { CLASS_HGUIDE, CLASS_VGUIDE, CLASS_MARK, CLASS_ARROW, NULL }; iArrow *iarrow = IARROW( iobject ); VipsBuf *buf = &iarrow->caption_buffer; const int nimages = g_slist_length( CLASSMODEL( iarrow )->iimages ); Expr *expr; gboolean result; gboolean first; int i; if( !HEAPMODEL( iarrow )->row || !(expr = HEAPMODEL( iarrow )->row->expr) || !heap_is_class( &expr->root, &result ) || !result ) return( _( "No image" ) ); vips_buf_rewind( buf ); heapmodel_name( HEAPMODEL( iarrow ), buf ); vips_buf_appendf( buf, " " ); /* Used in (eg.) "Mark at (10, 10) on [A1, A2]" */ vips_buf_appendf( buf, _( "on" ) ); vips_buf_appendf( buf, " " ); if( nimages > 1 ) vips_buf_appendf( buf, "[" ); first = TRUE; slist_map2( CLASSMODEL( iarrow )->iimages, (SListMap2Fn) iarrow_generate_caption_sub, iarrow, &first ); if( nimages > 1 ) vips_buf_appendf( buf, "]" ); vips_buf_appendf( buf, " " ); for( i = 0; names[i]; i++ ) { if( !heap_is_instanceof( names[i], &expr->root, &result ) ) break; if( result ) { switch( i ) { case 0: vips_buf_appendf( buf, _( "at %d" ), iarrow->instance.area.top ); break; case 1: vips_buf_appendf( buf, _( "at %d" ), iarrow->instance.area.left ); break; case 2: vips_buf_appendf( buf, _( "at (%d, %d)" ), iarrow->instance.area.left, iarrow->instance.area.top ); break; case 3: vips_buf_appendf( buf, _( "at (%d, %d), offset (%d, %d)" ), iarrow->instance.area.left, iarrow->instance.area.top, iarrow->instance.area.width, iarrow->instance.area.height ); break; default: g_assert( 0 ); } break; } } return( vips_buf_all( buf ) ); } static View * iarrow_view_new( Model *model, View *parent ) { return( valueview_new() ); } static void * iarrow_update_model( Heapmodel *heapmodel ) { /* Parent first ... this will update our instance vars. */ if( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ) return( heapmodel ); if( heapmodel->row->expr ) { iArrow *iarrow = IARROW( heapmodel ); /* Update who-has-displays-on-what stuff. */ classmodel_iimage_update( CLASSMODEL( iarrow ), iarrow->instance.ii ); /* Need to make sure the caption is regenerated. */ iobject_changed( IOBJECT( heapmodel ) ); } return( NULL ); } static void * iarrow_get_instance( Classmodel *classmodel ) { iArrow *iarrow = IARROW( classmodel ); return( &iarrow->instance ); } static void iarrow_class_init( iArrowClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; /* We share methods with iregion in a sort of MI way ... iregion needs * to be initialised before we can work. Force it to start up. */ (void) iregion_get_type(); parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = iarrow_finalize; iobject_class->generate_caption = iarrow_generate_caption; icontainer_class->parent_add = iregion_parent_add; model_class->view_new = iarrow_view_new; model_class->edit = iregion_edit; model_class->save = iregion_save; model_class->load = iregion_load; heapmodel_class->update_model = iarrow_update_model; heapmodel_class->update_heap = iregion_update_heap; classmodel_class->class_get = iregion_class_get; classmodel_class->class_new = iregion_class_new; classmodel_class->get_instance = iarrow_get_instance; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void iarrow_init( iArrow *iarrow ) { iregion_instance_init( &iarrow->instance, CLASSMODEL( iarrow ) ); vips_buf_init_dynamic( &iarrow->caption_buffer, MAX_LINELENGTH ); iobject_set( IOBJECT( iarrow ), CLASS_ARROW, NULL ); } GType iarrow_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( iArrowClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) iarrow_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( iArrow ), 32, /* n_preallocs */ (GInstanceInitFunc) iarrow_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "iArrow", &info, 0 ); } return( type ); } ================================================ FILE: src/iarrow.h ================================================ /* a ip region class in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IARROW (iarrow_get_type()) #define IARROW( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IARROW, iArrow )) #define IARROW_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_IARROW, iArrowClass)) #define IS_IARROW( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IARROW )) #define IS_IARROW_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IARROW )) #define IARROW_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_IARROW, iArrowClass )) struct _iArrow { Classmodel parent_class; /* Class fields. */ iRegionInstance instance; /* Private ... build iobject caption here. */ VipsBuf caption_buffer; }; typedef struct _iArrowClass { ClassmodelClass parent_class; /* My methods. */ } iArrowClass; GType iarrow_get_type( void ); ================================================ FILE: src/icontainer.c ================================================ /* abstract base class for containers */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_SANITY #define DEBUG_VERBOSE #define DEBUG */ #include "ip.h" /* Our signals. */ enum { SIG_POS_CHANGED, /* Member has moved */ SIG_CHILD_ADD, /* iContainer is about to gain a child */ SIG_CHILD_REMOVE, /* iContainer is about to loose a child */ SIG_CURRENT, /* Make child current of parent */ SIG_CHILD_DETACH, /* Used as a pair to do reparent */ SIG_CHILD_ATTACH, SIG_LAST }; static iObjectClass *parent_class = NULL; static guint icontainer_signals[SIG_LAST] = { 0 }; int icontainer_get_n_children( iContainer *icontainer ) { return( g_slist_length( icontainer->children ) ); } iContainer * icontainer_get_nth_child( iContainer *icontainer, int n ) { return( ICONTAINER( g_slist_nth_data( icontainer->children, n ) ) ); } GSList * icontainer_get_children( iContainer *icontainer ) { return( g_slist_copy( icontainer->children ) ); } void * icontainer_map( iContainer *icontainer, icontainer_map_fn fn, void *a, void *b ) { return( slist_map2( icontainer->children, (SListMap2Fn) fn, a, b ) ); } void * icontainer_map3( iContainer *icontainer, icontainer_map3_fn fn, void *a, void *b, void *c ) { return( slist_map3( icontainer->children, (SListMap3Fn) fn, a, b, c ) ); } void * icontainer_map4( iContainer *icontainer, icontainer_map4_fn fn, void *a, void *b, void *c, void *d ) { return( slist_map4( icontainer->children, (SListMap4Fn) fn, a, b, c, d ) ); } void * icontainer_map5( iContainer *icontainer, icontainer_map5_fn fn, void *a, void *b, void *c, void *d, void *e ) { return( slist_map5( icontainer->children, (SListMap5Fn) fn, a, b, c, d, e ) ); } /* Map in reverse order. */ void * icontainer_map_rev( iContainer *icontainer, icontainer_map_fn fn, void *a, void *b ) { return( slist_map2_rev( icontainer->children, (SListMap2Fn) fn, a, b ) ); } /* Apply a function to a tree of icontainers, bottom up. */ void * icontainer_map_all( iContainer *icontainer, icontainer_map_fn fn, void *a ) { iContainer *result; if( (result = icontainer_map( icontainer, (icontainer_map_fn) icontainer_map_all, (void *) fn, a )) ) return( result ); return( fn( icontainer, a, NULL ) ); } void * icontainer_map2_all( iContainer *icontainer, icontainer_map_fn fn, void *a, void *b ) { iContainer *result; if( (result = icontainer_map3( icontainer, (icontainer_map3_fn) icontainer_map2_all, (void *) fn, a, b )) ) return( result ); return( fn( icontainer, a, b ) ); } void * icontainer_map3_all( iContainer *icontainer, icontainer_map3_fn fn, void *a, void *b, void *c ) { iContainer *result; if( (result = icontainer_map4( icontainer, (icontainer_map4_fn) icontainer_map3_all, (void *) fn, a, b, c )) ) return( result ); return( fn( icontainer, a, b, c ) ); } void * icontainer_map4_all( iContainer *icontainer, icontainer_map4_fn fn, void *a, void *b, void *c, void *d ) { iContainer *result; if( (result = icontainer_map5( icontainer, (icontainer_map5_fn) icontainer_map4_all, (void *) fn, a, b, c, d )) ) return( result ); return( fn( icontainer, a, b, c, d ) ); } /* Apply a function to the children of a icontainer. */ void * icontainer_map_all_intrans( iContainer *icontainer, icontainer_map_fn fn, void *a ) { return( icontainer_map( icontainer, (icontainer_map_fn) icontainer_map_all, (void *) fn, a ) ); } static void * icontainer_sanity_child( iContainer *child, iContainer *parent ) { g_assert( IS_ICONTAINER( child ) ); g_assert( IS_ICONTAINER( parent ) ); g_assert( child->parent == parent ); g_assert( child->pos >= 0 ); g_assert( g_slist_find( parent->children, child ) ); if( parent->child_hash ) g_assert( g_hash_table_lookup( parent->child_hash, IOBJECT( child )->name ) ); return( NULL ); } void icontainer_sanity( iContainer *icontainer ) { g_assert( IS_ICONTAINER( icontainer ) ); if( icontainer->parent ) icontainer_sanity_child( icontainer, icontainer->parent ); icontainer_map( icontainer, (icontainer_map_fn) icontainer_sanity_child, icontainer, NULL ); } static gint icontainer_pos_compare( iContainer *a, iContainer *b ) { return( a->pos - b->pos ); } void icontainer_pos_sort( iContainer *icontainer ) { icontainer->children = g_slist_sort( icontainer->children, (GCompareFunc) icontainer_pos_compare ); iobject_changed( IOBJECT( icontainer ) ); } static void * icontainer_pos_last_sub( iContainer *icontainer, int *max ) { if( icontainer->pos > *max ) *max = icontainer->pos; return( NULL ); } int icontainer_pos_last( iContainer *icontainer ) { int max = -1; icontainer_map( icontainer, (icontainer_map_fn) icontainer_pos_last_sub, &max, NULL ); return( max ); } static void * icontainer_pos_changed( iContainer *icontainer ) { #ifdef DEBUG printf( "icontainer_pos_changed: " ); iobject_print( IOBJECT( icontainer ) ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( icontainer ), icontainer_signals[SIG_POS_CHANGED], 0 ); return( NULL ); } static void * icontainer_pos_renumber_sub( iContainer *icontainer, int *n, GSList **changed ) { if( icontainer->pos != *n ) { icontainer->pos = *n; *changed = g_slist_prepend( *changed, icontainer ); } *n += 1; return( NULL ); } #ifdef DEBUG_VERBOSE static void * icontainer_print_element( iContainer *element, int *n ) { printf( "\t%3d) pos = %d ", *n, element->pos ); iobject_print( IOBJECT( element ) ); *n += 1; return( NULL ); } #endif /*DEBUG_VERBOSE*/ void icontainer_pos_renumber( iContainer *icontainer ) { int n = 0; GSList *changed; #ifdef DEBUG_VERBOSE { int i; printf( "icontainer_pos_renumber: " ); iobject_print( IOBJECT( icontainer ) ); printf( "\tbefore:\n" ); i = 0; icontainer_map( icontainer, (icontainer_map_fn) icontainer_print_element, &i, NULL ); } #endif /*DEBUG_VERBOSE*/ changed = NULL; icontainer_map( icontainer, (icontainer_map_fn) icontainer_pos_renumber_sub, &n, &changed ); /* Tell all the children that have been renumbered. */ #ifdef DEBUG_VERBOSE if( g_slist_length( changed ) > 1 ) { printf( "icontainer_pos_renumber: renumbering %d children! ", g_slist_length( changed ) ); iobject_print( IOBJECT( icontainer ) ); } #endif /*DEBUG_VERBOSE*/ slist_map( changed, (SListMapFn) icontainer_pos_changed, NULL ); g_slist_free( changed ); iobject_changed( IOBJECT( icontainer ) ); #ifdef DEBUG_VERBOSE { int i; printf( "icontainer_pos_renumber: " ); iobject_print( IOBJECT( icontainer ) ); printf( "\tafter:\n" ); i = 0; icontainer_map( icontainer, (icontainer_map_fn) icontainer_print_element, &i, NULL ); } #endif /*DEBUG_VERBOSE*/ } gint icontainer_name_compare( iContainer *a, iContainer *b ) { return( strcasecmp( IOBJECT( a )->name, IOBJECT( b )->name ) ); } void icontainer_custom_sort( iContainer *icontainer, GCompareFunc fn ) { icontainer->children = g_slist_sort( icontainer->children, fn ); icontainer_pos_renumber( icontainer ); iobject_changed( IOBJECT( icontainer ) ); } /* Add a child. */ void icontainer_child_add( iContainer *parent, iContainer *child, int pos ) { g_assert( IS_ICONTAINER( parent ) ); g_assert( IS_ICONTAINER( child ) ); #ifdef DEBUG_SANITY icontainer_sanity( parent ); icontainer_sanity( child ); #endif /*DEBUG_SANITY*/ g_signal_emit( G_OBJECT( parent ), icontainer_signals[SIG_CHILD_ADD], 0, child, pos ); #ifdef DEBUG_SANITY icontainer_sanity( parent ); icontainer_sanity( child ); #endif /*DEBUG_SANITY*/ } /* Add a child before another child. after == NULL means append. */ void icontainer_child_add_before( iContainer *parent, iContainer *child, iContainer *before ) { int pos; g_assert( !before || IS_ICONTAINER( before ) ); g_assert( !before || before->parent == parent ); pos = g_slist_index( parent->children, before ); icontainer_child_add( parent, child, pos ); } /* pos == 0 ... move to start * pos == -1 ... move to end * pos == n ... move before sibling at position n */ void icontainer_child_move( iContainer *child, int pos ) { iContainer *parent = child->parent; parent->children = g_slist_remove( parent->children, child ); if( pos >= 0 ) parent->children = g_slist_insert( parent->children, child, pos ); else parent->children = g_slist_append( parent->children, child ); icontainer_pos_renumber( parent ); iobject_changed( IOBJECT( child ) ); } void * icontainer_child_remove( iContainer *child ) { iContainer *parent; if( (parent = child->parent) ) { g_assert( ICONTAINER_IS_CHILD( parent, child ) ); #ifdef DEBUG printf( "icontainer_child_remove: (child %p)\n", child ); printf( "\tchild: %s \"%s\"\n", G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ) ); #endif /*DEBUG*/ #ifdef DEBUG_SANITY icontainer_sanity( parent ); icontainer_sanity( child ); #endif /*DEBUG_SANITY*/ g_signal_emit( G_OBJECT( parent ), icontainer_signals[SIG_CHILD_REMOVE], 0, child ); #ifdef DEBUG_SANITY icontainer_sanity( parent ); #endif /*DEBUG_SANITY*/ } return( NULL ); } void icontainer_current( iContainer *parent, iContainer *child ) { g_assert( parent ); g_assert( !child || ICONTAINER_IS_CHILD( parent, child ) ); if( parent->current == child ) return; #ifdef DEBUG printf( "icontainer_current: (child %p)\n", child ); printf( "\tchild: %s \"%s\"\n", G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ) ); #endif /*DEBUG*/ #ifdef DEBUG_SANITY icontainer_sanity( parent ); if( child ) icontainer_sanity( child ); #endif /*DEBUG_SANITY*/ g_signal_emit( G_OBJECT( parent ), icontainer_signals[SIG_CURRENT], 0, child ); #ifdef DEBUG_SANITY icontainer_sanity( parent ); if( child ) icontainer_sanity( child ); #endif /*DEBUG_SANITY*/ } iContainer * icontainer_next( iContainer *parent ) { iContainer *child; int i; if( !parent->children ) return( NULL ); if( !parent->current ) i = 0; else i = g_slist_index( parent->children, parent->current ) + 1; if( !(child = g_slist_nth_data( parent->children, i )) ) child = ICONTAINER( parent->children->data ); icontainer_current( parent, child ); return( child ); } void icontainer_reparent( iContainer *parent, iContainer *child, int pos ) { iContainer *old_parent = child->parent; g_assert( parent ); g_assert( child ); #ifdef DEBUG_SANITY icontainer_sanity( old_parent ); icontainer_sanity( parent ); icontainer_sanity( child ); #endif /*DEBUG_SANITY*/ /* These must always happen as a pair. */ g_signal_emit( G_OBJECT( old_parent ), icontainer_signals[SIG_CHILD_DETACH], 0, child ); g_signal_emit( G_OBJECT( parent ), icontainer_signals[SIG_CHILD_ATTACH], 0, child, pos ); icontainer_pos_renumber( parent ); iobject_changed( IOBJECT( parent ) ); iobject_changed( IOBJECT( old_parent ) ); iobject_changed( IOBJECT( child ) ); #ifdef DEBUG_SANITY icontainer_sanity( old_parent ); icontainer_sanity( parent ); icontainer_sanity( child ); #endif /*DEBUG_SANITY*/ } static void icontainer_dispose( GObject *gobject ) { iContainer *icontainer; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_ICONTAINER( gobject ) ); icontainer = ICONTAINER( gobject ); #ifdef DEBUG printf( "icontainer_dispose: (%p) %s \"%s\"\n", icontainer, G_OBJECT_TYPE_NAME( icontainer ), NN( IOBJECT( icontainer )->name ) ); #endif /*DEBUG*/ icontainer_map( icontainer, (icontainer_map_fn) icontainer_child_remove, NULL, NULL ); icontainer_child_remove( icontainer ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void icontainer_finalize( GObject *gobject ) { iContainer *icontainer; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_ICONTAINER( gobject ) ); icontainer = ICONTAINER( gobject ); IM_FREEF( g_hash_table_destroy, icontainer->child_hash ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void icontainer_info( iObject *iobject, VipsBuf *buf ) { iContainer *icontainer = ICONTAINER( iobject ); vips_buf_appendf( buf, "pos = \"%d\"\n", icontainer->pos ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); } static void icontainer_real_pos_changed( iContainer *icontainer ) { } static void icontainer_link( iContainer *parent, iContainer *child, int pos ) { if( pos >= 0 ) parent->children = g_slist_insert( parent->children, child, pos ); else parent->children = g_slist_append( parent->children, child ); child->parent = parent; child->pos = pos; if( parent->child_hash ) { g_assert( !g_hash_table_lookup( parent->child_hash, IOBJECT( child )->name ) ); g_hash_table_insert( parent->child_hash, IOBJECT( child )->name, child ); } } static void icontainer_real_child_add( iContainer *parent, iContainer *child, int pos ) { iContainerClass *icontainer_class = ICONTAINER_GET_CLASS( child ); g_assert( IS_ICONTAINER( parent ) && IS_ICONTAINER( child ) ); g_assert( child->parent == NULL ); #ifdef DEBUG printf( "icontainer_real_child_add:\n\tparent " ); iobject_print( IOBJECT( parent ) ); printf( "\tchild " ); iobject_print( IOBJECT( child ) ); printf( "\tpos = %d\n", pos ); #endif /*DEBUG*/ icontainer_link( parent, child, pos ); g_object_ref( G_OBJECT( child ) ); iobject_sink( IOBJECT( child ) ); /* Renumber to get all the pos set. */ icontainer_pos_renumber( parent ); iobject_changed( IOBJECT( child ) ); /* We've made the link ... trigger the parent_add() on the child. */ icontainer_class->parent_add( child ); #ifdef DEBUG_VERBOSE printf( "icontainer_real_child_add: " ); iobject_print( IOBJECT( parent ) ); #endif /*DEBUG_VERBOSE*/ } static void icontainer_unlink( iContainer *child ) { iContainer *parent = child->parent; parent->children = g_slist_remove( parent->children, child ); child->parent = NULL; if( parent->child_hash ) { g_assert( g_hash_table_lookup( parent->child_hash, IOBJECT( child )->name ) ); g_hash_table_remove( parent->child_hash, IOBJECT( child )->name ); } } static void icontainer_real_child_remove( iContainer *parent, iContainer *child ) { iContainerClass *icontainer_child_class = ICONTAINER_GET_CLASS( child ); g_assert( IS_ICONTAINER( parent ) && IS_ICONTAINER( child ) ); #ifdef DEBUG printf( "icontainer_real_child_remove: parent %s \"%s\"; " "child %s \"%s\"\n", G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ), G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ) ); #endif /*DEBUG*/ if( parent->current == child ) icontainer_current( parent, NULL ); /* We're about to break the link ... trigger the parent_remove() on * the child. */ icontainer_child_class->parent_remove( child ); icontainer_unlink( child ); UNREF( child ); iobject_changed( IOBJECT( parent ) ); } static void icontainer_real_parent_add( iContainer *child ) { #ifdef DEBUG printf( "icontainer_real_parent_add: child %s \"%s\"; " "parent %s \"%s\"\n", G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ), G_OBJECT_TYPE_NAME( child->parent ), NN( IOBJECT( child->parent )->name ) ); #endif /*DEBUG*/ } static void icontainer_real_parent_remove( iContainer *child ) { #ifdef DEBUG { iContainer *parent = child->parent; printf( "icontainer_real_parent_remove: child %s \"%s\"; " "parent %s \"%s\"\n", G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ), G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) ); } #endif /*DEBUG*/ } static void icontainer_real_current( iContainer *parent, iContainer *child ) { iContainer *old_current; g_assert( IS_ICONTAINER( parent ) ); g_assert( !child || IS_ICONTAINER( child ) ); g_assert( !child || ICONTAINER_IS_CHILD( parent, child ) ); #ifdef DEBUG printf( "icontainer_real_current: parent %s \"%s\"; " "child %s \"%s\"\n", G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ), child ? G_OBJECT_TYPE_NAME( child ) : "NULL", child ? NN( IOBJECT( child )->name ) : "NULL" ); #endif /*DEBUG*/ old_current = parent->current; parent->current = child; if( old_current != child ) { if( old_current ) iobject_changed( IOBJECT( old_current ) ); if( child ) iobject_changed( IOBJECT( child ) ); iobject_changed( IOBJECT( parent ) ); } if( child ) model_front( MODEL( child ) ); } static void icontainer_real_child_detach( iContainer *parent, iContainer *child ) { g_assert( IS_ICONTAINER( parent ) ); g_assert( IS_ICONTAINER( child ) ); g_assert( child->parent != NULL ); g_assert( ICONTAINER_IS_CHILD( parent, child ) ); icontainer_unlink( child ); } static void icontainer_real_child_attach( iContainer *parent, iContainer *child, int pos ) { g_assert( IS_ICONTAINER( parent ) ); g_assert( IS_ICONTAINER( child ) ); g_assert( child->parent == NULL ); icontainer_link( parent, child, pos ); } static void icontainer_class_init( iContainerClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = icontainer_dispose; gobject_class->finalize = icontainer_finalize; iobject_class->info = icontainer_info; class->pos_changed = icontainer_real_pos_changed; class->child_add = icontainer_real_child_add; class->child_remove = icontainer_real_child_remove; class->parent_add = icontainer_real_parent_add; class->parent_remove = icontainer_real_parent_remove; class->current = icontainer_real_current; class->child_detach = icontainer_real_child_detach; class->child_attach = icontainer_real_child_attach; /* Create signals. */ icontainer_signals[SIG_POS_CHANGED] = g_signal_new( "pos_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( iContainerClass, pos_changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); icontainer_signals[SIG_CHILD_ADD] = g_signal_new( "child_add", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( iContainerClass, child_add ), NULL, NULL, nip_VOID__OBJECT_INT, G_TYPE_NONE, 2, TYPE_ICONTAINER, GTK_TYPE_INT ); icontainer_signals[SIG_CHILD_REMOVE] = g_signal_new( "child_remove", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( iContainerClass, child_remove ), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, TYPE_ICONTAINER ); icontainer_signals[SIG_CURRENT] = g_signal_new( "current", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( iContainerClass, current ), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, TYPE_ICONTAINER ); icontainer_signals[SIG_CHILD_DETACH] = g_signal_new( "child_detach", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( iContainerClass, child_detach ), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, TYPE_ICONTAINER ); icontainer_signals[SIG_CHILD_ATTACH] = g_signal_new( "child_attach", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( iContainerClass, child_attach ), NULL, NULL, nip_VOID__OBJECT_INT, G_TYPE_NONE, 2, TYPE_ICONTAINER, GTK_TYPE_INT ); #ifdef DEBUG_SANITY printf( "*** DEBUG_SANITY is on ... expect slowness\n" ); #endif /*DEBUG_SANITY*/ } static void icontainer_init( iContainer *icontainer ) { /* Init our instance fields. */ icontainer->children = NULL; icontainer->pos = -1; icontainer->parent = NULL; icontainer->child_hash = NULL; } GType icontainer_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( iContainerClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) icontainer_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( iContainer ), 32, /* n_preallocs */ (GInstanceInitFunc) icontainer_init, }; type = g_type_register_static( TYPE_IOBJECT, "iContainer", &info, 0 ); } return( type ); } /* Put the container into lookup-by-child-name mode. */ void icontainer_set_hash( iContainer *icontainer ) { /* Can only do this once just after startup, and before there are any * children. */ g_assert( !icontainer->children ); g_assert( !icontainer->child_hash ); icontainer->child_hash = g_hash_table_new( g_str_hash, g_str_equal ); } iContainer * icontainer_child_lookup( iContainer *parent, const char *name ) { g_assert( parent->child_hash ); return( ICONTAINER( g_hash_table_lookup( parent->child_hash, name ) ) ); } ================================================ FILE: src/icontainer.h ================================================ /* abstract base class for containers */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_ICONTAINER (icontainer_get_type()) #define ICONTAINER( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_ICONTAINER, iContainer )) #define ICONTAINER_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_ICONTAINER, iContainerClass)) #define IS_ICONTAINER( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_ICONTAINER )) #define IS_ICONTAINER_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_ICONTAINER )) #define ICONTAINER_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_ICONTAINER, iContainerClass )) /* Test for is C a child of P. */ #define ICONTAINER_IS_CHILD( P, C ) \ (g_slist_find( ICONTAINER( P )->children, ICONTAINER( C ) ) && \ ICONTAINER( C )->parent == ICONTAINER( P )) struct _iContainer { iObject parent_object; /* My instance vars. */ GSList *children; /* iContainers which are inside this one */ int pos; /* Position in parent */ iContainer *parent; /* iContainer we are inside */ GHashTable *child_hash; /* Optional: hash of children by their name */ /* Can have a currently selected child ... eg. the * current column in a workspace, or the current tab in a * workspacegroup. * * NULL if not relevant. */ iContainer *current; /* Track the view here during reparent. */ View *temp_view; }; typedef struct _iContainerClass { iObjectClass parent_class; /* pos_changed our pos has changed child_add a child has been added to us child_remove a child is about be removed from us parent_add parent has been attached parent_remove parent is about to be removed current make the current of parent child_detach on old parent, unlink child child_attach on new_paerent, link on child there are used as a pair to do reparent -- the old parent gets a chance to detach in ::parent_detach, the new parent attaches in ::child_attach */ void (*pos_changed)( iContainer *icontainer ); void (*child_add)( iContainer *parent, iContainer *child, int ); void (*child_remove)( iContainer *parent, iContainer *child ); void (*parent_add)( iContainer *child ); void (*parent_remove)( iContainer *child ); void (*current)( iContainer *parent, iContainer *child ); void (*child_detach)( iContainer *parent, iContainer *child ); void (*child_attach)( iContainer *parent, iContainer *child, int ); } iContainerClass; typedef void *(*icontainer_map_fn)( iContainer *, void *, void * ); typedef void *(*icontainer_map3_fn)( iContainer *, void *, void *, void * ); typedef void *(*icontainer_map4_fn)( iContainer *, void *, void *, void *, void * ); typedef void *(*icontainer_map5_fn)( iContainer *, void *, void *, void *, void *, void * ); typedef gint (*icontainer_sort_fn)( iContainer *a, iContainer *b ); int icontainer_get_n_children( iContainer *icontainer ); iContainer *icontainer_get_nth_child( iContainer *icontainer, int n ); GSList *icontainer_get_children( iContainer *icontainer ); void *icontainer_map( iContainer *icontainer, icontainer_map_fn fn, void *a, void *b ); void *icontainer_map3( iContainer *icontainer, icontainer_map3_fn fn, void *a, void *b, void *c ); void *icontainer_map4( iContainer *icontainer, icontainer_map4_fn fn, void *a, void *b, void *c, void *d ); void *icontainer_map5( iContainer *icontainer, icontainer_map5_fn fn, void *a, void *b, void *c, void *d, void *e ); void *icontainer_map_rev( iContainer *icontainer, icontainer_map_fn fn, void *a, void *b ); void *icontainer_map_all( iContainer *icontainer, icontainer_map_fn fn, void *a ); void *icontainer_map2_all( iContainer *icontainer, icontainer_map_fn fn, void *a, void *b ); void *icontainer_map3_all( iContainer *icontainer, icontainer_map3_fn fn, void *a, void *b, void *c ); void *icontainer_map4_all( iContainer *icontainer, icontainer_map4_fn fn, void *a, void *b, void *c, void *d ); void *icontainer_map_all_intrans( iContainer *icontainer, icontainer_map_fn fn, void *a ); void icontainer_sanity( iContainer *icontainer ); void icontainer_pos_sort( iContainer *icontainer ); int icontainer_pos_last( iContainer *icontainer ); void icontainer_pos_renumber( iContainer *icontainer ); void icontainer_custom_sort( iContainer *icontainer, GCompareFunc fn ); gint icontainer_name_compare( iContainer *a, iContainer *b ); void icontainer_child_add( iContainer *icontainer, iContainer *child, int pos ); void icontainer_child_add_before( iContainer *parent, iContainer *child, iContainer *before ); void icontainer_child_move( iContainer *child, int pos ); void *icontainer_child_remove( iContainer *child ); void icontainer_current( iContainer *parent, iContainer *child ); iContainer *icontainer_next( iContainer *parent ); void icontainer_reparent( iContainer *parent, iContainer *child, int pos ); GType icontainer_get_type( void ); void icontainer_set_hash( iContainer *icontainer ); iContainer *icontainer_child_lookup( iContainer *parent, const char *name ); ================================================ FILE: src/idialog.c ================================================ /* make and manage base dialogs ... subclass off this for others */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static iWindowClass *parent_class = NULL; /* An OK button: label (can be a stock) plus a callback. */ typedef struct { char *label; iWindowFn done_cb; } OKButton; static void * okbutton_free( OKButton *ok ) { IM_FREEF( g_free, ok->label ); ok->done_cb = NULL; IM_FREEF( g_free, ok ); return( NULL ); } static OKButton * okbutton_new( char *label, iWindowFn done_cb ) { OKButton *ok; ok = g_new( OKButton, 1 ); ok->label = g_strdup( label ); ok->done_cb = done_cb; return( ok ); } /* Handy destroy callback ... just free client. */ void idialog_free_client( iDialog *idlg, void *client ) { IM_FREE( client ); } /* Notify our parent. */ static void idialog_notify_parent( iDialog *idlg, iWindowResult result ) { if( idlg->nfn ) { iWindowNotifyFn nfn = idlg->nfn; idlg->nfn = NULL; nfn( idlg->sys, result ); } } static void * idialog_set_sensitive( GtkWidget *w, gboolean state ) { gtk_widget_set_sensitive( w, state ); return( NULL ); } /* Set OK sensitivities. */ void idialog_set_ok_button_state( iDialog *idlg, gboolean state ) { slist_map( idlg->ok_but_l, (SListMapFn) idialog_set_sensitive, GINT_TO_POINTER( state ) ); } /* Set all the button sensitivities. */ static void idialog_set_button_state( iDialog *idlg, gboolean state ) { idialog_set_ok_button_state( idlg, state ); if( idlg->but_cancel ) gtk_widget_set_sensitive( idlg->but_cancel, state ); if( idlg->but_help ) gtk_widget_set_sensitive( idlg->but_help, state ); } /* Sub-fn of below. Come back from a popdown notify. */ static void idialog_popdown_notify( void *sys, iWindowResult result ) { iWindowSusp *susp = IWINDOW_SUSP( sys ); iDialog *idlg = IDIALOG( susp->client ); if( result == IWINDOW_YES ) /* If our caller hasn't been notified yet, post off a FALSE. */ idialog_notify_parent( idlg, IWINDOW_NO ); /* Pass result on to our suspension (ie. back to iwindow). */ iwindow_susp_return( susp, result ); /* Housekeeping. */ iwindow_notify_return( IWINDOW( idlg ) ); } /* Our popdown callback ... here from iwindow. */ static void idialog_popdown_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { iDialog *idlg = IDIALOG( client ); iWindowSusp *susp = iwindow_susp_new( NULL, iwnd, idlg, nfn, sys ); #ifdef DEBUG printf( "idialog_popdown_cb: %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ /* Trigger user popdown. */ iwindow_notify_send( IWINDOW( idlg ), idlg->popdown_cb, idlg->client, idialog_popdown_notify, susp ); } /* Sub-fn of below. Come back from a done notify. */ static void idialog_done_notify( void *sys, iWindowResult result ) { iDialog *idlg = IDIALOG( sys ); #ifdef DEBUG printf( "idialog_done_notify: %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ idialog_set_button_state( idlg, TRUE ); /* If all ok, popdown and tell our parent. */ if( result == IWINDOW_YES ) { /* Unless we're pinned up, that is. */ if( !(idlg->tog_pin && gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( idlg->tog_pin ) )) ) { idialog_notify_parent( idlg, result ); iwindow_kill( IWINDOW( idlg ) ); } } /* Alert on failure. */ if( result == IWINDOW_ERROR ) iwindow_alert( GTK_WIDGET( idlg ), GTK_MESSAGE_ERROR ); /* Clean up. */ iwindow_notify_return( IWINDOW( idlg ) ); } /* Make a DONE event happen. Used (for example) by the browse window to force * a done in the enclosing FSB on double click on icon. */ void idialog_done_trigger( iDialog *idlg, int pos ) { OKButton *ok = (OKButton *) g_slist_nth_data( idlg->ok_disp_l, pos ); #ifdef DEBUG printf( "idialog_done_trigger: %s, %d\n", IWINDOW( idlg )->title, pos ); #endif /*DEBUG*/ /* Trigger user done callback. */ g_assert( pos >= 0 ); g_assert( ok->done_cb ); idialog_set_button_state( idlg, FALSE ); iwindow_notify_send( IWINDOW( idlg ), ok->done_cb, idlg->client, idialog_done_notify, idlg ); } /* Sub-fn of below. */ static void idialog_cancel_notify( void *sys, iWindowResult result ) { iDialog *idlg = IDIALOG( sys ); #ifdef DEBUG printf( "idialog_cancel_notify: %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ idialog_set_button_state( idlg, TRUE ); /* Send cancel message back to parent if our client cancel was OK. */ if( result == IWINDOW_YES ) { idialog_notify_parent( idlg, IWINDOW_NO ); iwindow_kill( IWINDOW( idlg ) ); } /* Alert on error. */ if( result == IWINDOW_ERROR ) iwindow_alert( GTK_WIDGET( idlg ), GTK_MESSAGE_ERROR ); /* Clean up. */ iwindow_notify_return( IWINDOW( idlg ) ); } static void idialog_cancel_trigger( iDialog *idlg ) { #ifdef DEBUG printf( "idialog_cancel_trigger: %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ /* Trigger user cancel function. */ idialog_set_button_state( idlg, FALSE ); iwindow_notify_send( IWINDOW( idlg ), idlg->cancel_cb, idlg->client, idialog_cancel_notify, idlg ); } /* Button callbacks from gtk. */ static void idialog_done_cb( GtkWidget *w, iDialog *idlg ) { int pos = g_slist_index( idlg->ok_but_l, w ); g_assert( pos != -1 ); idialog_done_trigger( idlg, pos ); } static void idialog_cancel_cb( GtkWidget *w, iDialog *idlg ) { idialog_cancel_trigger( idlg ); } static void idialog_help_cb( GtkWidget *w, iDialog *idlg ) { if( idlg->help_tag ) box_help( GTK_WIDGET( idlg ), idlg->help_tag ); } static void idialog_destroy( GtkObject *object ) { iDialog *idlg; #ifdef DEBUG printf( "idialog_destroy\n" ); #endif /*DEBUG*/ g_return_if_fail( object != NULL ); g_return_if_fail( IS_IDIALOG( object ) ); idlg = IDIALOG( object ); #ifdef DEBUG printf( "... %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ /* My instance destroy stuff. */ if( idlg->destroy_cb ) { idlg->destroy_cb( idlg, idlg->client ); idlg->destroy_cb = NULL; } FREESID( idlg->destroy_sid, idlg->iobject ); slist_map( idlg->ok_l, (SListMapFn) okbutton_free, NULL ); IM_FREEF( g_slist_free, idlg->ok_l ); IM_FREEF( g_slist_free, idlg->ok_disp_l ); IM_FREEF( g_slist_free, idlg->ok_but_l ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void idialog_realize( GtkWidget *widget ) { iDialog *idlg = IDIALOG( widget ); #ifdef DEBUG printf( "idialog_realize: %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ GTK_WIDGET_CLASS( parent_class )->realize( widget ); if( idlg->entry ) gtk_widget_grab_focus( GTK_WIDGET( idlg->entry ) ); } /* The object we represent has been destroyed, kill us too. */ static void idialog_iobject_destroy( iObject *iobject, iDialog *idlg ) { #ifdef DEBUG printf( "idialog_iobject_destroy: %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ /* This object has gone. */ idlg->iobject = NULL; iwindow_kill( IWINDOW( idlg ) ); } static void * idialog_build_ok( OKButton *ok, iDialog *idlg ) { GtkWidget *but; but = build_button( ok->label, GTK_SIGNAL_FUNC( idialog_done_cb ), idlg ); idlg->ok_disp_l = g_slist_prepend( idlg->ok_disp_l, ok ); idlg->ok_but_l = g_slist_prepend( idlg->ok_but_l, but ); gtk_box_pack_start( GTK_BOX( idlg->bb ), but, TRUE, TRUE, 0 ); gtk_widget_show( but ); return( NULL ); } static void * idialog_build_cancel( iDialog *idlg ) { idlg->but_cancel = build_button( idlg->cancel_text, GTK_SIGNAL_FUNC( idialog_cancel_cb ), idlg ); gtk_box_pack_start( GTK_BOX( idlg->bb ), idlg->but_cancel, TRUE, TRUE, 0 ); gtk_widget_show( idlg->but_cancel ); return( NULL ); } /* Set a button to be the dialog default. Turn off button_focus for complex * dialogs like file_chooser which have their on focus systems. */ static void idialog_set_default( iDialog *idlg, GtkWidget *widget ) { if( idlg->button_focus ) gtk_widget_grab_focus( widget ); GTK_WIDGET_SET_FLAGS( widget, GTK_CAN_DEFAULT ); gtk_window_set_default( GTK_WINDOW( idlg ), widget ); } static void idialog_build( GtkWidget *widget ) { iDialog *idlg = IDIALOG( widget ); iWindow *iwnd = IWINDOW( idlg ); #ifdef DEBUG printf( "idialog_build: %s\n", iwnd->title ); #endif /*DEBUG*/ /* Call all builds in superclasses. */ if( IWINDOW_CLASS( parent_class )->build ) (*IWINDOW_CLASS( parent_class )->build)( widget ); /* delete_event and destroy handled by our superclass. */ iwindow_set_popdown( iwnd, idialog_popdown_cb, idlg ); gtk_window_set_modal( GTK_WINDOW( idlg ), idlg->modal ); idlg->work = gtk_vbox_new( FALSE, 6 ); gtk_container_set_border_width( GTK_CONTAINER( idlg->work ), 12 ); gtk_box_pack_start( GTK_BOX( iwnd->work ), idlg->work, TRUE, TRUE, 0 ); if( !idlg->nosep ) { GtkWidget *sep; sep = gtk_hseparator_new(); gtk_box_pack_start( GTK_BOX( iwnd->work ), sep, FALSE, FALSE, 2 ); gtk_widget_show( sep ); } idlg->hb = gtk_hbox_new( FALSE, 6 ); gtk_container_set_border_width( GTK_CONTAINER( idlg->hb ), 12 ); gtk_box_pack_start( GTK_BOX( iwnd->work ), idlg->hb, FALSE, FALSE, 0 ); gtk_widget_show( idlg->hb ); if( idlg->pinup ) { idlg->tog_pin = gtk_check_button_new_with_label( _( "Pin up" ) ); set_tooltip( idlg->tog_pin, _( "Check this to pin the dialog up" ) ); gtk_box_pack_start( GTK_BOX( idlg->hb ), idlg->tog_pin, FALSE, FALSE, 0 ); gtk_widget_show( idlg->tog_pin ); } idlg->bb = gtk_hbutton_box_new(); gtk_button_box_set_layout( GTK_BUTTON_BOX( idlg->bb ), GTK_BUTTONBOX_END ); gtk_box_set_spacing( GTK_BOX( idlg->bb ), 6 ); gtk_box_pack_end( GTK_BOX( idlg->hb ), idlg->bb, FALSE, FALSE, 0 ); gtk_widget_show( idlg->bb ); /* Default button order: * * Help OK3 OK2 Cancel OK1 * * win32 button order: * * OK1 OK2 OK3 Cancel Help */ #ifdef OS_WIN32 /* OK buttons. */ slist_map( idlg->ok_l, (SListMapFn) idialog_build_ok, idlg ); if( idlg->cancel_cb ) { idialog_build_cancel( idlg ); /* Cancel grabs default if it's the only button. Set focus * too; user build can change this later. */ if( !idlg->ok_l ) idialog_set_default( idlg, idlg->but_cancel ); } if( idlg->help_tag ) { idlg->but_help = build_button( GTK_STOCK_HELP, GTK_SIGNAL_FUNC( idialog_help_cb ), idlg ); gtk_widget_show( idlg->but_help ); } #else /*!OS_WIN32*/ if( idlg->help_tag ) { idlg->but_help = build_button( GTK_STOCK_HELP, GTK_SIGNAL_FUNC( idialog_help_cb ), idlg ); gtk_box_pack_end( GTK_BOX( idlg->bb ), idlg->but_help, TRUE, TRUE, 0 ); gtk_button_box_set_child_secondary( GTK_BUTTON_BOX( idlg->bb ), idlg->but_help, TRUE ); gtk_widget_show( idlg->but_help ); } /* Add OK2, 3, etc. */ if( idlg->ok_l && idlg->ok_l->next ) slist_map_rev( idlg->ok_l->next, (SListMapFn) idialog_build_ok, idlg ); if( idlg->cancel_cb ) { idialog_build_cancel( idlg ); /* Cancel grabs default if it's the only button. Set focus * too; user build can change this later. */ if( !idlg->ok_l ) idialog_set_default( idlg, idlg->but_cancel ); } /* Make OK1 */ if( idlg->ok_l ) { OKButton *ok1 = (OKButton *) idlg->ok_l->data; idialog_build_ok( ok1, idlg ); } #endif /*lots*/ /* OK1 grabs the default. */ if( idlg->ok_but_l ) idialog_set_default( idlg, idlg->ok_but_l->data ); /* Escape triggers cancel, if there is a cancel. */ if( idlg->cancel_cb ) gtk_widget_add_accelerator( idlg->but_cancel, "clicked", iwnd->accel_group, GDK_Escape, 0, 0 ); else { /* If there's just 1 OK, that gets Esc too. */ if( idlg->ok_but_l && g_slist_length( idlg->ok_but_l ) == 1 ) gtk_widget_add_accelerator( GTK_WIDGET( idlg->ok_but_l->data ), "clicked", iwnd->accel_group, GDK_Escape, 0, 0 ); } /* F1 triggers help. */ if( idlg->but_help ) gtk_widget_add_accelerator( idlg->but_help, "clicked", iwnd->accel_group, GDK_F1, 0, 0 ); /* Build user dialog contents. */ if( idlg->build ) idlg->build( iwnd, idlg->work, idlg->build_a, idlg->build_b, idlg->build_c ); if( idlg->iobject ) idlg->destroy_sid = g_signal_connect( idlg->iobject, "destroy", G_CALLBACK( idialog_iobject_destroy ), idlg ); gtk_widget_show( idlg->work ); } static void idialog_class_init( iDialogClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; iWindowClass *iwindow_class = (iWindowClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = idialog_destroy; widget_class->realize = idialog_realize; iwindow_class->build = idialog_build; iwindow_class->transient = TRUE; /* Create signals. */ /* Init methods. */ } static void idialog_init( iDialog *idlg ) { #ifdef DEBUG printf( "idialog_init: %s\n", IWINDOW( idlg )->title ); #endif /*DEBUG*/ /* Init our instance fields. */ idlg->iobject = NULL; idlg->destroy_sid = 0; idlg->work = NULL; idlg->ok_l = NULL; idlg->ok_disp_l = NULL; idlg->ok_but_l = NULL; idlg->but_cancel = NULL; idlg->but_help = NULL; idlg->tog_pin = NULL; idlg->entry = NULL; idlg->modal = FALSE; idlg->pinup = FALSE; idlg->nosep = FALSE; idlg->button_focus = TRUE; idlg->help_tag = NULL; idlg->cancel_text = GTK_STOCK_CANCEL; idlg->cancel_cb = NULL; idlg->popdown_cb = NULL; idlg->destroy_cb = NULL; idlg->client = NULL; idlg->arg = NULL; idlg->nfn = iwindow_notify_null; idlg->sys = NULL; gtk_window_set_position( GTK_WINDOW( idlg ), GTK_WIN_POS_CENTER_ON_PARENT ); } GtkType idialog_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "iDialog", sizeof( iDialog ), sizeof( iDialogClass ), (GtkClassInitFunc) idialog_class_init, (GtkObjectInitFunc) idialog_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_IWINDOW, &info ); } return( type ); } GtkWidget * idialog_new() { iDialog *idlg = gtk_type_new( TYPE_IDIALOG ); GtkWindow *gwnd = GTK_WINDOW( idlg ); /* Init gtk base class. */ gwnd->type = GTK_WINDOW_TOPLEVEL; return( GTK_WIDGET( idlg ) ); } void idialog_set_iobject( iDialog *idlg, iObject *iobject ) { idlg->iobject = iobject; } void idialog_set_pinup( iDialog *idlg, gboolean pinup ) { idlg->pinup = pinup; if( idlg->tog_pin ) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( idlg->tog_pin ), TRUE ); } void idialog_set_modal( iDialog *idlg, gboolean modal ) { idlg->modal = modal; } void idialog_set_nosep( iDialog *idlg, gboolean nosep ) { idlg->nosep = nosep; } void idialog_set_button_focus( iDialog *idlg, gboolean button_focus ) { idlg->button_focus = button_focus; } void idialog_set_help_tag( iDialog *idlg, const char *help_tag ) { IM_SETSTR( idlg->help_tag, help_tag ); } void idialog_set_callbacks( iDialog *idlg, iWindowFn cancel_cb, iWindowFn popdown_cb, iDialogFreeFn destroy_cb, void *client ) { idlg->cancel_cb = cancel_cb; idlg->popdown_cb = popdown_cb; idlg->destroy_cb = destroy_cb; idlg->client = client; } void idialog_add_ok( iDialog *idlg, iWindowFn done_cb, const char *fmt, ... ) { va_list ap; char buf[1024]; va_start( ap, fmt ); (void) im_vsnprintf( buf, 1024, fmt, ap ); va_end( ap ); /* So the last OK button added is the default one (and at the head of * the list). [OK1, OK2, OK3, OK4] */ idlg->ok_l = g_slist_prepend( idlg->ok_l, okbutton_new( buf, done_cb ) ); } void idialog_set_notify( iDialog *idlg, iWindowNotifyFn nfn, void *sys ) { idlg->nfn = nfn; idlg->sys = sys; } void idialog_set_build( iDialog *idlg, iWindowBuildFn build, void *build_a, void *build_b, void *build_c ) { idlg->build = build; idlg->build_a = build_a; idlg->build_b = build_b; idlg->build_c = build_c; } void idialog_set_cancel_text( iDialog *idlg, const char *cancel_text ) { idlg->cancel_text = cancel_text; } void idialog_set_default_entry( iDialog *idlg, GtkEntry *entry ) { gtk_entry_set_activates_default( entry, TRUE ); idlg->entry = entry; } /* Set up an entry inside a dialog ... set tooltip, set start * value, link to OK button in enclosing dialog. */ void idialog_init_entry( iDialog *idlg, GtkWidget *entry, const char *tip, const char *fmt, ... ) { va_list ap; va_start( ap, fmt ); set_gentryv( entry, fmt, ap ); va_end( ap ); set_tooltip( entry, "%s", tip ); idialog_set_default_entry( idlg, GTK_ENTRY( entry ) ); } ================================================ FILE: src/idialog.h ================================================ /* make and manage dialogs ... subclass off this for dialog boxes */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #ifndef IDIALOG_H #define IDIALOG_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define TYPE_IDIALOG (idialog_get_type()) #define IDIALOG( obj ) (GTK_CHECK_CAST( (obj), TYPE_IDIALOG, iDialog )) #define IDIALOG_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IDIALOG, iDialogClass )) #define IS_IDIALOG( obj ) (GTK_CHECK_TYPE( (obj), TYPE_IDIALOG )) #define IS_IDIALOG_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IDIALOG )) typedef struct _iDialog iDialog; typedef void (*iDialogFreeFn)( iDialog *, void * ); struct _iDialog { iWindow parent_object; /* My instance vars. */ iObject *iobject; /* Kill dialog if this obj goes */ guint destroy_sid; /* Signal id for obj destroy */ GtkWidget *work; /* Our work area */ GtkWidget *hb; GtkWidget *bb; GSList *ok_l; /* List of OKbutton as set by user */ GSList *ok_disp_l; /* List of OKbutton as displayed */ GSList *ok_but_l; /* List of OK GtkButton as displayed */ GtkWidget *but_cancel; GtkWidget *but_help; GtkWidget *tog_pin; /* Optional pinup widget */ GtkEntry *entry; /* Last entry we added as default */ /* Flags. */ gboolean modal; /* Modal/non-modal */ gboolean pinup; /* Stay up on OK */ gboolean nosep; /* Suppress hseparator */ gboolean button_focus; /* TRUE to focus buttons */ /* Name of help tag ... if set, make a help button and link to display * of this. */ char *help_tag; /* What we label the cancel button as (if any). Usually * GTK_STOCK_CANCEL, but instant-apply dialogs should change this to * GTK_STOCK_CLOSE. */ const char *cancel_text; /* Per-instance build function. */ iWindowBuildFn build; void *build_a, *build_b, *build_c; /* Our callbacks. */ iWindowFn cancel_cb; iWindowFn popdown_cb; iDialogFreeFn destroy_cb; /* Called from _destroy() */ void *client; /* Client data for callbacks */ void *arg; /* Misc thing provided to client */ /* Notify our parent when we finish. */ iWindowNotifyFn nfn; void *sys; }; typedef struct _iDialogClass { iWindowClass parent_class; /* Our methods. */ } iDialogClass; void idialog_free_client( iDialog *idlg, void *client ); void idialog_set_ok_button_state( iDialog *idlg, gboolean state ); GtkType idialog_get_type( void ); GtkWidget *idialog_new( void ); void idialog_set_iobject( iDialog *idlg, iObject *iobject ); void idialog_set_modal( iDialog *, gboolean ); void idialog_set_pinup( iDialog *idlg, gboolean pinup ); void idialog_set_nosep( iDialog *, gboolean ); void idialog_set_button_focus( iDialog *idlg, gboolean button_focus ); void idialog_set_help_tag( iDialog *, const char *help_tag ); void idialog_set_callbacks( iDialog *, iWindowFn cancel_cb, iWindowFn popdown_cb, iDialogFreeFn destroy_cb, void *client ); void idialog_add_ok( iDialog *, iWindowFn done_cb, const char *fmt, ... ) __attribute__((format(printf, 3, 4))); void idialog_set_notify( iDialog *, iWindowNotifyFn, void * ); void idialog_set_build( iDialog *, iWindowBuildFn, void *, void *, void * ); void idialog_set_cancel_text( iDialog *, const char *cancel_text ); void idialog_set_default_entry( iDialog *idlg, GtkEntry *entry ); void idialog_init_entry( iDialog *idlg, GtkWidget *entry, const char *tip, const char *fmt, ... ) __attribute__((format(printf, 4, 5))); void idialog_done_trigger( iDialog *idlg, int pos ); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* IDIALOG_H */ ================================================ FILE: src/iimage.c ================================================ /* an image class object in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void iimage_dispose( GObject *gobject ) { iImage *iimage; #ifdef DEBUG printf( "iimage_dispose %p\n", gobject ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_IIMAGE( gobject ) ); iimage = IIMAGE( gobject ); slist_map( iimage->classmodels, (SListMapFn) classmodel_iimage_unlink, iimage ); g_assert( !iimage->classmodels ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void iimage_finalize( GObject *gobject ) { iImage *iimage; #ifdef DEBUG printf( "iimage_finalize\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_IIMAGE( gobject ) ); iimage = IIMAGE( gobject ); image_value_destroy( &iimage->value ); IM_FREEF( g_slist_free, iimage->views ); vips_buf_destroy( &iimage->caption_buffer ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Return the main caption. */ static const char * iimage_generate_caption( iObject *iobject ) { iImage *iimage = IIMAGE( iobject ); Imageinfo *ii = iimage->value.ii; VipsBuf *buf = &iimage->caption_buffer; vips_buf_rewind( buf ); image_value_caption( &iimage->value, buf ); if( ii ) { vips_buf_appends( buf, ", " ); iobject_info( IOBJECT( iimage->value.ii ), buf ); } return( vips_buf_all( buf ) ); } static void iimage_info( iObject *iobject, VipsBuf *buf ) { iImage *iimage = IIMAGE( iobject ); Imageinfo *ii = iimage->value.ii; IMAGE *im; if( ii && (im = imageinfo_get( FALSE, ii )) ) { char *filename; if( im_header_get_typeof( im, ORIGINAL_FILENAME ) != 0 ) { if( !im_header_string( im, ORIGINAL_FILENAME, &filename ) ) { vips_buf_appends( buf, _( "Original filename" ) ); vips_buf_appendf( buf, ": %s\n", filename ); } } } } static View * iimage_view_new( Model *model, View *parent ) { return( iimageview_new() ); } static void iimage_edit( GtkWidget *parent, Model *model ) { iImage *iimage = IIMAGE( model ); if( iimage->value.ii ) (void) imageview_new( iimage, parent ); } void iimage_header( GtkWidget *parent, Model *model ) { iImage *iimage = IIMAGE( model ); Row *row = HEAPMODEL( iimage )->row; Workspace *ws = row_get_workspace( row ); GtkWidget *imageheader; char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); imageheader = imageheader_new( iimage ); row_qualified_name_relative( ws->sym, row, &buf ); iwindow_set_title( IWINDOW( imageheader ), _( "Header for \"%s\"" ), vips_buf_all( &buf ) ); idialog_set_callbacks( IDIALOG( imageheader ), NULL, NULL, NULL, NULL ); idialog_add_ok( IDIALOG( imageheader ), iwindow_true_cb, _( "OK" ) ); iwindow_set_parent( IWINDOW( imageheader ), parent ); idialog_set_iobject( IDIALOG( imageheader ), IOBJECT( iimage ) ); iwindow_build( IWINDOW( imageheader ) ); gtk_widget_show( imageheader ); } static xmlNode * iimage_save( Model *model, xmlNode *xnode ) { iImage *iimage = IIMAGE( model ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); /* We always rebuild the value from the expr ... don't save. */ if( !set_iprop( xthis, "image_left", iimage->image_left ) || !set_iprop( xthis, "image_top", iimage->image_top ) || !set_iprop( xthis, "image_mag", iimage->image_mag ) || !set_sprop( xthis, "show_status", bool_to_char( iimage->show_status ) ) || !set_sprop( xthis, "show_paintbox", bool_to_char( iimage->show_paintbox ) ) || !set_sprop( xthis, "show_convert", bool_to_char( iimage->show_convert ) ) || !set_sprop( xthis, "show_rulers", bool_to_char( iimage->show_rulers ) ) || !set_dprop( xthis, "scale", iimage->scale ) || !set_dprop( xthis, "offset", iimage->offset ) || !set_sprop( xthis, "falsecolour", bool_to_char( iimage->falsecolour ) ) || !set_sprop( xthis, "type", bool_to_char( iimage->type ) ) ) return( NULL ); return( xthis ); } static gboolean iimage_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { iImage *iimage = IIMAGE( model ); g_assert( IS_RHS( parent ) ); (void) get_iprop( xnode, "image_left", &iimage->image_left ); (void) get_iprop( xnode, "image_top", &iimage->image_top ); (void) get_iprop( xnode, "image_mag", &iimage->image_mag ); (void) get_bprop( xnode, "show_status", &iimage->show_status ); (void) get_bprop( xnode, "show_paintbox", &iimage->show_paintbox ); (void) get_bprop( xnode, "show_convert", &iimage->show_convert ); (void) get_bprop( xnode, "show_rulers", &iimage->show_rulers ); (void) get_dprop( xnode, "scale", &iimage->scale ); (void) get_dprop( xnode, "offset", &iimage->offset ); (void) get_bprop( xnode, "falsecolour", &iimage->falsecolour ); (void) get_bprop( xnode, "type", &iimage->type ); return( MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ); } /* Need to implement _update_heap(), as not all model fields are directly * editable ... some are set only from expr. See also iregion.c. */ static void * iimage_update_heap( Heapmodel *heapmodel ) { Expr *expr = heapmodel->row->expr; iImage *iimage = IIMAGE( heapmodel ); ImageValue *value = &iimage->value; PElement pe; Imageinfo *ii; #ifdef DEBUG printf( "iimage_update_heap: " ); row_name_print( HEAPMODEL( iimage )->row ); printf( "\n" ); #endif /*DEBUG*/ /* Read the heap into the model, over the top of the unapplied edits. */ if( !class_get_exact( &expr->root, IOBJECT( heapmodel )->name, &pe ) ) return( FALSE ); if( !class_get_member_image( &pe, MEMBER_VALUE, &ii ) ) return( FALSE ); image_value_set( value, ii ); IM_FREE( CLASSMODEL( iimage )->filename ); if( value->ii && imageinfo_is_from_file( value->ii ) ) IM_SETSTR( CLASSMODEL( iimage )->filename, IOBJECT( value->ii )->name ); /* Classmodel _update_heap() will do _instance_new() from the fixed up * model. */ return( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) ); } /* Update iImage from heap. */ static gboolean iimage_class_get( Classmodel *classmodel, PElement *root ) { iImage *iimage = IIMAGE( classmodel ); ImageValue *value = &iimage->value; Imageinfo *ii; #ifdef DEBUG printf( "iimage_class_get: " ); row_name_print( HEAPMODEL( iimage )->row ); printf( "\n" ); #endif /*DEBUG*/ if( !class_get_member_image( root, MEMBER_VALUE, &ii ) ) return( FALSE ); image_value_set( value, ii ); /* Try to update the filename for this row ... get from the meta if we * can. */ IM_FREE( classmodel->filename ); if( ii ) { IMAGE *im; char *filename; if( (im = imageinfo_get( FALSE, ii )) && im_header_get_typeof( im, ORIGINAL_FILENAME ) != 0 ) { if( im_header_string( im, ORIGINAL_FILENAME, &filename ) ) return( FALSE ); } else if( imageinfo_is_from_file( ii ) ) filename = IOBJECT( ii )->name; else filename = NULL; IM_SETSTR( classmodel->filename, filename ); } return( CLASSMODEL_CLASS( parent_class )->class_get( classmodel, root ) ); } /* Make a new "fn value" application. */ static gboolean iimage_class_new( Classmodel *classmodel, PElement *fn, PElement *out ) { Heap *heap = reduce_context->heap; iImage *iimage = IIMAGE( classmodel ); ImageValue *value = &iimage->value; PElement rhs; #ifdef DEBUG printf( "iimage_class_new: " ); row_name_print( HEAPMODEL( iimage )->row ); printf( "\n" ); #endif /*DEBUG*/ /* Make application nodes. */ heap_appl_init( out, fn ); if( !heap_appl_add( heap, out, &rhs ) ) return( FALSE ); PEPUTP( &rhs, ELEMENT_MANAGED, value->ii ); return( TRUE ); } static gboolean iimage_graphic_save( Classmodel *classmodel, GtkWidget *parent, const char *filename ) { iImage *iimage = IIMAGE( classmodel ); ImageValue *value = &iimage->value; char buf[FILENAME_MAX]; /* Can't happen nested-ly, so a static is OK. */ static GTimer *timer = NULL; /* We don't want $VAR etc. in the filename we pass down to the file * ops. */ im_strncpy( buf, filename, FILENAME_MAX ); path_expand( buf ); /* Append the mode string. This needs an expanded filename. */ filesel_add_mode( buf ); if( !timer ) timer = g_timer_new(); g_timer_reset( timer ); if( value->ii ) if( !imageinfo_write( value->ii, buf ) ) return( FALSE ); mainw_recent_add( &mainw_recent_image, filename ); if( main_option_time_save ) { double elapsed; elapsed = g_timer_elapsed( timer, NULL ); error_top( _( "Save timer." ) ); error_sub( _( "Image save took %g seconds." ), elapsed ); return( FALSE ); } return( TRUE ); } gboolean iimage_replace( iImage *iimage, const char *filename ) { Row *row = HEAPMODEL( iimage )->row; iText *itext = ITEXT( HEAPMODEL( iimage )->rhs->itext ); char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_appends( &buf, "Image_file \"" ); vips_buf_appendsc( &buf, TRUE, filename ); vips_buf_appends( &buf, "\"" ); if( itext_set_formula( itext, vips_buf_all( &buf ) ) ) { itext_set_edited( itext, TRUE ); workspace_set_modified( row->ws, TRUE ); (void) expr_dirty( row->expr, link_serial_new() ); mainw_recent_add( &mainw_recent_image, filename ); } return( TRUE ); } static gboolean iimage_graphic_replace( Classmodel *classmodel, GtkWidget *parent, const char *filename ) { return( iimage_replace( IIMAGE( classmodel ), filename ) ); } static void iimage_class_init( iImageClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = iimage_dispose; gobject_class->finalize = iimage_finalize; iobject_class->user_name = _( "Image" ); iobject_class->generate_caption = iimage_generate_caption; iobject_class->info = iimage_info; model_class->view_new = iimage_view_new; model_class->edit = iimage_edit; model_class->header = iimage_header; model_class->save = iimage_save; model_class->load = iimage_load; heapmodel_class->update_heap = iimage_update_heap; classmodel_class->class_get = iimage_class_get; classmodel_class->class_new = iimage_class_new; classmodel_class->graphic_save = iimage_graphic_save; classmodel_class->graphic_replace = iimage_graphic_replace; classmodel_class->filetype = filesel_type_image; classmodel_class->filetype_pref = "IMAGE_FILE_TYPE"; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void iimage_init( iImage *iimage ) { image_value_init( &iimage->value, CLASSMODEL( iimage ) ); iimage->classmodels = NULL; iimage->views = NULL; iimage->image_left = 0; iimage->image_top = 0; iimage->image_mag = 0; iimage->show_status = FALSE; iimage->show_paintbox = FALSE; iimage->show_convert = FALSE; iimage->show_rulers = FALSE; iimage->scale = 0.0; iimage->offset = 0.0; iimage->falsecolour = FALSE; iimage->type = TRUE; vips_buf_init_dynamic( &iimage->caption_buffer, MAX_LINELENGTH ); iobject_set( IOBJECT( iimage ), CLASS_IMAGE, NULL ); } GtkType iimage_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( iImageClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) iimage_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( iImage ), 32, /* n_preallocs */ (GInstanceInitFunc) iimage_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "iImage", &info, 0 ); } return( type ); } ================================================ FILE: src/iimage.h ================================================ /* a ip image class in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IIMAGE (iimage_get_type()) #define IIMAGE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IIMAGE, iImage )) #define IIMAGE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_IIMAGE, iImageClass)) #define IS_IIMAGE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IIMAGE )) #define IS_IIMAGE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IIMAGE )) #define IIMAGE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_IIMAGE, iImageClass )) struct _iImage { Classmodel parent_class; /* Class fields. */ ImageValue value; /* List of classmodel which have displays on us. */ GSList *classmodels; /* List of popup imageview windows we've made. */ GSList *views; /* Track display pos/size/etc. here. */ int image_left, image_top; /* Scroll position */ int image_mag; /* Scale */ /* View attachments. */ gboolean show_status; gboolean show_paintbox; gboolean show_convert; gboolean show_rulers; /* Bar settings we remember. */ double scale, offset; gboolean falsecolour; gboolean type; /* Private ... build iobject caption here. */ VipsBuf caption_buffer; }; typedef struct _iImageClass { ClassmodelClass parent_class; /* My methods. */ } iImageClass; GType iimage_get_type( void ); gboolean iimage_replace( iImage *iimage, const char *filename ); void iimage_header( GtkWidget *parent, Model *model ); ================================================ FILE: src/iimageview.c ================================================ /* run the display for an image in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; static void iimageview_realize( GtkWidget *widget ) { GTK_WIDGET_CLASS( parent_class )->realize( widget ); /* Mark us as a symbol drag-to widget. */ set_symbol_drag_type( widget ); } GtkWidget * iimageview_drag_window_new( int width, int height ) { GtkWidget *window; window = gtk_window_new( GTK_WINDOW_POPUP ); gtk_widget_set_app_paintable( GTK_WIDGET( window ), TRUE ); gtk_widget_set_size_request( window, width, height ); gtk_widget_realize( window ); #ifdef HAVE_SET_OPACITY gdk_window_set_opacity( window->window, 0.5 ); #endif /*HAVE_SET_OPACITY*/ return( window ); } static void iimageview_drag_begin( GtkWidget *widget, GdkDragContext *context ) { iImageview *iimageview = IIMAGEVIEW( widget ); Conversion *conv = iimageview->conv; GtkWidget *window; Imagedisplay *id; #ifdef DEBUG printf( "iimageview_drag_begin: \n" ); #endif /*DEBUG*/ window = iimageview_drag_window_new( conv->canvas.width, conv->canvas.height ); gtk_object_set_data_full( GTK_OBJECT( widget ), "nip2-drag-window", window, (GtkDestroyNotify) gtk_widget_destroy ); id = imagedisplay_new( conv ); gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( id ) ); gtk_widget_show( GTK_WIDGET( id ) ); gtk_drag_set_icon_widget( context, window, -2, -2 ); } static void iimageview_drag_end( GtkWidget *widget, GdkDragContext *context ) { #ifdef DEBUG printf( "iimageview_drag_end:\n" ); #endif /*DEBUG*/ gtk_object_set_data( GTK_OBJECT( widget ), "nip2-drag-window", NULL ); } static void iimageview_drag_data_get( GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time ) { #ifdef DEBUG printf( "iimageview_drag_data_get:\n" ); #endif /*DEBUG*/ if( info == TARGET_SYMBOL ) { iImageview *iimageview = IIMAGEVIEW( widget ); iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject ); Row *row = HEAPMODEL( iimage )->row; char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); /* Drag the fully-qualified row name. */ row_qualified_name_relative( main_workspaceroot->sym, row, &buf ); gtk_selection_data_set( selection_data, gdk_atom_intern( "text/symbol", FALSE ), 8, (guchar *) vips_buf_all( &buf ), strlen( vips_buf_all( &buf ) ) ); } } static void iimageview_drag_data_received( GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time ) { #ifdef DEBUG printf( "iimageview_drag_data_received:\n" ); #endif /*DEBUG*/ if( info == TARGET_SYMBOL && selection_data->length > 0 && selection_data->format == 8 ) { const char *from_row_path = (const char *) selection_data->data; iImageview *iimageview = IIMAGEVIEW( widget ); iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject ); Row *row = HEAPMODEL( iimage )->row; Row *from_row; #ifdef DEBUG printf( " seen TARGET_SYMBOL \"%s\"\n", from_row_path ); #endif /*DEBUG*/ /* Block drags to ourselves ... pointless. */ if( (from_row = row_parse_name( main_workspaceroot->sym, from_row_path )) && from_row != row ) { iText *itext = ITEXT( HEAPMODEL( iimage )->rhs->itext ); char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); /* Qualify relative to us. We don't want to embed * workspace names unless we have to. */ if( row->top_row->sym ) row_qualified_name_relative( row->top_row->sym, from_row, &buf ); if( itext_set_formula( itext, vips_buf_all( &buf ) ) ) { itext_set_edited( itext, TRUE ); (void) expr_dirty( row->expr, link_serial_new() ); workspace_set_modified( row->ws, TRUE ); symbol_recalculate_all(); } /* Usually the drag-from row will be selected, very * annoying. Select the drag-to row. */ row_select( row ); } } } /* Not the same as model->edit :-( if this is a region, don't pop the region * edit box, pop a viewer on the image. */ static void iimageview_edit( GtkWidget *parent, iImageview *iimageview ) { iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject ); if( IS_IREGION( iimage ) && iimage->value.ii ) imageview_new( iimage, parent ); else model_edit( parent, MODEL( iimage ) ); } static void iimageview_link( View *view, Model *model, View *parent ) { iImageview *iimageview = IIMAGEVIEW( view ); Rowview *rview; VIEW_CLASS( parent_class )->link( view, model, parent ); if( (rview = ROWVIEW( parent->parent )) ) { Row *row = ROW( VOBJECT( rview )->iobject ); rowview_menu_attach( rview, GTK_WIDGET( iimageview->id ) ); if( row->popup && row->top_row == row ) { row->popup = FALSE; iimageview_edit( GTK_WIDGET( view ), iimageview ); } } } static void iimageview_refresh( vObject *vobject ) { iImageview *iimageview = IIMAGEVIEW( vobject ); iImage *iimage = IIMAGE( vobject->iobject ); Row *row = HEAPMODEL( iimage )->row; int w, h; gboolean enabled; double scale, offset; gboolean falsecolour, type; #ifdef DEBUG printf( "iimageview_refresh\n" ); #endif /*DEBUG*/ w = IM_MAX( GTK_WIDGET( iimageview->id )->requisition.width, DISPLAY_THUMBNAIL ); h = DISPLAY_THUMBNAIL; conversion_set_image( iimageview->conv, iimage->value.ii ); gtk_widget_set_size_request( GTK_WIDGET( iimageview->id ), w, h ); gtk_widget_queue_draw( GTK_WIDGET( iimageview->id ) ); set_gcaption( iimageview->label, "%s", NN( IOBJECT( iimage )->caption ) ); /* Set scale/offset for the thumbnail. Use the prefs setting, or if * there's a setting for this image, override with that. */ enabled = DISPLAY_CONVERSION; scale = row->ws->scale; offset = row->ws->offset; falsecolour = FALSE; type = TRUE; /* If the image_width has been set, a viewer must have popped down and * set it, so the recorded settings must be valid. */ if( MODEL( iimage )->window_width != -1 ) { enabled = iimage->show_convert; scale = iimage->scale; offset = iimage->offset; falsecolour = iimage->falsecolour; type = iimage->type; } conversion_set_params( iimageview->conv, enabled, scale, offset, falsecolour, type ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void iimageview_class_init( iImageviewClass *class ) { GtkWidgetClass *widget_class = (GtkWidgetClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ widget_class->realize = iimageview_realize; widget_class->drag_begin = iimageview_drag_begin; widget_class->drag_end = iimageview_drag_end; widget_class->drag_data_get = iimageview_drag_data_get; widget_class->drag_data_received = iimageview_drag_data_received; vobject_class->refresh = iimageview_refresh; view_class->link = iimageview_link; } static void iimageview_doubleclick_one_cb( GtkWidget *widget, GdkEvent *event, iImageview *iimageview ) { Heapmodel *heapmodel = HEAPMODEL( VOBJECT( iimageview )->iobject ); Row *row = heapmodel->row; row_select_modifier( row, event->button.state ); } static void iimageview_doubleclick_two_cb( GtkWidget *widget, GdkEvent *event, iImageview *iimageview ) { iimageview_edit( widget, iimageview ); } static gboolean iimageview_filedrop( iImageview *iimageview, const char *file ) { iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject ); gboolean result; if( (result = iimage_replace( iimage, file )) ) symbol_recalculate_all(); return( result ); } static void iimageview_tooltip_generate( GtkWidget *widget, VipsBuf *buf, iImageview *iimageview ) { iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject ); Imageinfo *ii = iimage->value.ii; IMAGE *im = imageinfo_get( FALSE, ii ); vips_buf_rewind( buf ); vips_buf_appends( buf, vips_buf_all( &iimage->caption_buffer ) ); if( im ) { double size = (double) im->Ysize * IM_IMAGE_SIZEOF_LINE( im ); vips_buf_appends( buf, ", " ); vips_buf_append_size( buf, size ); vips_buf_appendf( buf, ", %.3gx%.3g p/mm", im->Xres, im->Yres ); } } static void iimageview_init( iImageview *iimageview ) { GtkWidget *eb; GtkWidget *vbox; #ifdef DEBUG printf( "iimageview_init\n" ); #endif /*DEBUG*/ eb = gtk_event_box_new(); gtk_box_pack_start( GTK_BOX( iimageview ), eb, FALSE, FALSE, 0 ); vbox = gtk_vbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( eb ), vbox ); gtk_widget_show( vbox ); iimageview->conv = conversion_new( NULL ); iimageview->conv->tile_size = 16; iimageview->id = imagedisplay_new( iimageview->conv ); imagedisplay_set_shrink_to_fit( iimageview->id, TRUE ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( iimageview->id ), FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( iimageview->id ) ); /* Need these events in the enclosing workspaceview. */ gtk_widget_add_events( GTK_WIDGET( iimageview->id ), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ); iimageview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( iimageview->label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( iimageview->label ), 2, 0 ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( iimageview->label ), FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( iimageview->label ) ); /* Set as file drop destination */ filedrop_register( GTK_WIDGET( iimageview ), (FiledropFunc) iimageview_filedrop, iimageview ); doubleclick_add( GTK_WIDGET( iimageview ), FALSE, DOUBLECLICK_FUNC( iimageview_doubleclick_one_cb ), iimageview, DOUBLECLICK_FUNC( iimageview_doubleclick_two_cb ), iimageview ); set_tooltip_generate( eb, (TooltipGenerateFn) iimageview_tooltip_generate, iimageview, NULL ); gtk_widget_set_name( eb, "caption_widget" ); gtk_widget_show( GTK_WIDGET( eb ) ); } GtkType iimageview_get_type( void ) { static GtkType iimageview_type = 0; if( !iimageview_type ) { static const GtkTypeInfo info = { "iImageview", sizeof( iImageview ), sizeof( iImageviewClass ), (GtkClassInitFunc) iimageview_class_init, (GtkObjectInitFunc) iimageview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; iimageview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( iimageview_type ); } View * iimageview_new( void ) { iImageview *iimageview = gtk_type_new( TYPE_IIMAGEVIEW ); return( VIEW( iimageview ) ); } ================================================ FILE: src/iimageview.h ================================================ /* a iimageview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IIMAGEVIEW (iimageview_get_type()) #define IIMAGEVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_IIMAGEVIEW, iImageview )) #define IIMAGEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IIMAGEVIEW, iImageviewClass )) #define IS_IIMAGEVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_IIMAGEVIEW )) #define IS_IIMAGEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IIMAGEVIEW )) typedef struct _iImageview { Graphicview parent_object; guint popup_sid; /* id for popup menu */ Imagedisplay *id; Conversion *conv; GtkWidget *label; } iImageview; typedef struct _iImageviewClass { GraphicviewClass parent_class; /* My methods. */ } iImageviewClass; GtkWidget *iimageview_drag_window_new( int width, int height ); GtkType iimageview_get_type( void ); View *iimageview_new( void ); ================================================ FILE: src/imagedisplay.c ================================================ /* Imagedisplay widget code ... display entire image, place this widget in a * scrolledwindow to get clipping/scrolling behaviour. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Trace painting actions #define DEBUG_PAINT */ /* #define DEBUG_GEO */ #include "ip.h" enum { SIG_AREA_CHANGED, /* xywh area changed, canvas cods */ SIG_LAST }; static GtkDrawingAreaClass *parent_class = NULL; static guint imagedisplay_signals[SIG_LAST] = { 0 }; /* Handy! */ void imagedisplay_queue_draw_area( Imagedisplay *id, Rect *area ) { #ifdef DEBUG_PAINT printf( "imagedisplay_queue_draw_area: " "left = %d, top = %d, width = %d, height = %d\n", area->left, area->top, area->width, area->height ); #endif /*DEBUG_PAINT*/ gtk_widget_queue_draw_area( GTK_WIDGET( id ), area->left, area->top, area->width, area->height ); } /* Repaint an area of the image. */ static void imagedisplay_paint_image( Imagedisplay *id, Rect *area ) { Conversion *conv = id->conv; guchar *buf; int lsk; #ifdef DEBUG_PAINT g_print( "imagedisplay_paint_image: at %d x %d, size %d x %d ", area->left, area->top, area->width, area->height ); gobject_print( G_OBJECT( id ) ); #endif /*DEBUG_PAINT*/ /* Request pixels. We ask the mask first, to get an idea of what's * currently in cache, then request tiles of pixels. We must always * request pixels, even if the mask is blank, because the request * will trigger a notify later which will reinvoke us. */ if( conv->mreg && im_prepare( conv->mreg, area ) ) { #ifdef DEBUG_PAINT printf( "imagedisplay_paint_image: mask paint error\n" ); printf( "\t%s\n", im_error_buffer() ); #endif /*DEBUG_PAINT*/ return; } if( im_prepare( conv->ireg, area ) ) { #ifdef DEBUG_PAINT printf( "imagedisplay_paint_image: paint error\n" ); printf( "\t%s\n", im_error_buffer() ); #endif /*DEBUG_PAINT*/ im_error_clear(); return; } /* Is the mask all zero? Skip the paint. */ if( conv->mreg ) { gboolean found; int x, y; buf = (guchar *) IM_REGION_ADDR( conv->mreg, area->left, area->top ); lsk = IM_REGION_LSKIP( conv->mreg ); found = FALSE; for( y = 0; y < area->height; y++ ) { for( x = 0; x < area->width; x++ ) if( buf[x] ) { found = TRUE; break; } if( found ) break; buf += lsk; } if( !found ) { #ifdef DEBUG_PAINT printf( "imagedisplay_paint_image: zero mask\n" ); #endif /*DEBUG_PAINT*/ return; } } /* Paint into window. */ buf = (guchar *) IM_REGION_ADDR( conv->ireg, area->left, area->top ); lsk = IM_REGION_LSKIP( conv->ireg ); if( conv->ireg->im->Bands == 3 ) gdk_draw_rgb_image( GTK_WIDGET( id )->window, GTK_WIDGET( id )->style->white_gc, area->left, area->top, area->width, area->height, GDK_RGB_DITHER_MAX, buf, lsk ); else if( conv->ireg->im->Bands == 1 ) gdk_draw_gray_image( GTK_WIDGET( id )->window, GTK_WIDGET( id )->style->white_gc, area->left, area->top, area->width, area->height, GDK_RGB_DITHER_MAX, buf, lsk ); } /* Paint an area with the background pattern. */ static void imagedisplay_paint_background( Imagedisplay *id, Rect *expose ) { #ifdef DEBUG_PAINT g_print( "imagedisplay_paint_background: at %d x %d, size %d x %d\n", expose->left, expose->top, expose->width, expose->height ); #endif /*DEBUG_PAINT*/ gdk_draw_rectangle( GTK_WIDGET( id )->window, id->back_gc, TRUE, expose->left, expose->top, expose->width, expose->height ); } /* Paint areas outside the image. */ static void imagedisplay_paint_background_clipped( Imagedisplay *id, Rect *expose ) { Conversion *conv = id->conv; Rect clip; #ifdef DEBUG_PAINT g_print( "imagedisplay_paint_background_clipped: canvas %d x %d\n", conv->canvas.width, conv->canvas.height ); #endif /*DEBUG_PAINT*/ /* If the expose touches the image, we cut it into two parts: * everything to the right of the image, and everything strictly * below. */ im_rect_intersectrect( expose, &conv->canvas, &clip ); if( !im_rect_isempty( &clip ) ) { Rect area; area = *expose; area.left = conv->canvas.width; area.width -= clip.width; if( area.width > 0 ) imagedisplay_paint_background( id, &area ); area = *expose; area.top = conv->canvas.height; area.width = clip.width; area.height -= clip.height; if( area.height > 0 ) imagedisplay_paint_background( id, &area ); } else imagedisplay_paint_background( id, expose ); } static void imagedisplay_paint( Imagedisplay *id, Rect *area ) { Conversion *conv = id->conv; const int tsize = conv->tile_size; Rect clip; int xs, ys; int x, y; /* There's no image to paint. */ if( !conv->ireg ) return; /* Clip non-image parts of the expose. */ im_rect_intersectrect( area, &conv->canvas, &clip ); if( im_rect_isempty( &clip ) ) return; #ifdef DEBUG_PAINT g_print( "imagedisplay_paint: at %d x %d, size %d x %d\n", clip.left, clip.top, clip.width, clip.height ); #endif /*DEBUG_PAINT*/ /* Round left/top down to the start tile. */ xs = (clip.left / tsize) * tsize; ys = (clip.top / tsize) * tsize; /* Now loop painting image tiles. */ for( y = ys; y < IM_RECT_BOTTOM( &clip ); y += tsize ) for( x = xs; x < IM_RECT_RIGHT( &clip ); x += tsize ) { Rect tile; Rect tile2; tile.left = x; tile.top = y; tile.width = conv->tile_size; tile.height = conv->tile_size; im_rect_intersectrect( &tile, &clip, &tile2 ); imagedisplay_paint_image( id, &tile2 ); } } /* Expose signal handler. */ static gint imagedisplay_expose( GtkWidget *widget, GdkEventExpose *event ) { Imagedisplay *id = IMAGEDISPLAY( widget ); GdkRectangle *rect; int i, n; if( !GTK_WIDGET_DRAWABLE( id ) || event->area.width == 0 || event->area.height == 0 || !GTK_WIDGET( id )->window || !GTK_WIDGET_VISIBLE( id ) ) return( FALSE ); gdk_region_get_rectangles( event->region, &rect, &n ); #ifdef DEBUG_PAINT g_print( "imagedisplay_expose: %d rectangles\n", n ); #endif /*DEBUG_PAINT*/ for( i = 0; i < n; i++ ) { Rect area; area.left = rect[i].x; area.top = rect[i].y; area.width = rect[i].width; area.height = rect[i].height; /* Clear to background. Always do this, to make sure we paint * outside the image area. */ imagedisplay_paint_background_clipped( id, &area ); /* And paint pixels. */ imagedisplay_paint( id, &area ); } g_free( rect ); return( FALSE ); } /* Resize signal. */ static gboolean imagedisplay_configure_event( GtkWidget *widget, GdkEventConfigure *event ) { Imagedisplay *id = IMAGEDISPLAY( widget ); #ifdef DEBUG_GEO g_print( "imagedisplay_configure_event: %d x %d:\n", event->width, event->height ); #endif /*DEBUG_GEO*/ /* Note new size in visible hint. Except if parent is a viewport ... * if it's a viewport, someone else will have to track the visible * area. */ if( !GTK_IS_VIEWPORT( gtk_widget_get_parent( widget ) ) ) { id->conv->visible.width = event->width; id->conv->visible.height = event->height; } /* Recalculate shrink to fit, if necessary. */ if( id->shrink_to_fit ) { #ifdef DEBUG_GEO g_print( "imagedisplay_configure_event_cb: shrink-to-fit\n" ); #endif /*DEBUG_GEO*/ conversion_set_mag( id->conv, 0 ); } return( FALSE ); } static void imagedisplay_destroy( GtkObject *object ) { Imagedisplay *id = IMAGEDISPLAY( object ); #ifdef DEBUG g_print( "imagedisplay_destroy: " ); gobject_print( G_OBJECT( id ) ); #endif /*DEBUG*/ FREESID( id->changed_sid, id->conv ); FREESID( id->area_changed_sid, id->conv ); UNREF( id->conv ); UNREF( id->back_gc ); UNREF( id->top_gc ); UNREF( id->bottom_gc ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* Conversion has changed ... resize to fit. */ static void imagedisplay_real_conversion_changed( Imagedisplay *id ) { GtkRequisition *requisition = >K_WIDGET( id )->requisition; Rect *canvas = &id->conv->canvas; g_assert( IS_IMAGEDISPLAY( id ) ); #ifdef DEBUG g_print( "imagedisplay_real_conversion_changed: " ); gobject_print( G_OBJECT( id ) ); #endif /*DEBUG*/ /* If we're in shrink-to-fit mode, do a shrink. * Otherwise resize to hold the new image. */ if( id->shrink_to_fit ) conversion_set_mag( id->conv, 0 ); else if( requisition->width != canvas->width || requisition->height != canvas->height ) { #ifdef DEBUG_GEO g_print( "imagedisplay_real_conversion_" "changed: requesting new size " "%d x %d\n", id->conv->canvas.width, id->conv->canvas.height ); #endif /*DEBUG_GEO*/ requisition->width = canvas->width; requisition->height = canvas->height; gtk_widget_queue_resize( GTK_WIDGET( id ) ); } } static void imagedisplay_real_area_changed( Imagedisplay *id, Rect *dirty ) { imagedisplay_queue_draw_area( id, dirty ); } static void imagedisplay_realize( GtkWidget *widget ) { Imagedisplay *id = IMAGEDISPLAY( widget ); GdkColor fg, bg; GTK_WIDGET_CLASS( parent_class )->realize( widget ); gdk_window_set_back_pixmap( widget->window, NULL, FALSE ); gtk_widget_set_double_buffered( widget, FALSE ); id->back_gc = gdk_gc_new( widget->window ); fg.red = fg.green = fg.blue = 0x90 << 8; bg.red = bg.green = bg.blue = 0xA0 << 8; gdk_gc_set_rgb_fg_color( id->back_gc, &fg ); gdk_gc_set_rgb_bg_color( id->back_gc, &bg ); id->top_gc = gdk_gc_new( widget->window ); id->bottom_gc = gdk_gc_new( widget->window ); } /* Init Imagedisplay class. */ static void imagedisplay_class_init( ImagedisplayClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = imagedisplay_destroy; widget_class->expose_event = imagedisplay_expose; widget_class->configure_event = imagedisplay_configure_event; widget_class->realize = imagedisplay_realize; class->conversion_changed = imagedisplay_real_conversion_changed; class->area_changed = imagedisplay_real_area_changed; imagedisplay_signals[SIG_AREA_CHANGED] = g_signal_new( "area_changed", G_OBJECT_CLASS_TYPE( class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ImagedisplayClass, area_changed ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER ); } static void imagedisplay_init( Imagedisplay *id ) { id->conv = NULL; id->changed_sid = 0; id->area_changed_sid = 0; id->shrink_to_fit = FALSE; id->back_gc = NULL; id->top_gc = NULL; id->bottom_gc = NULL; } GType imagedisplay_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ImagedisplayClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) imagedisplay_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Imagedisplay ), 32, /* n_preallocs */ (GInstanceInitFunc) imagedisplay_init, }; type = g_type_register_static( GTK_TYPE_DRAWING_AREA, "Imagedisplay", &info, 0 ); } return( type ); } /* Conversion has changed ... repaint everything. */ static void imagedisplay_conversion_changed_cb( Conversion *conv, Imagedisplay *id ) { #ifdef DEBUG printf( "imagedisplay_conversion_changed_cb: " ); gobject_print( G_OBJECT( id ) ); #endif /*DEBUG*/ IMAGEDISPLAY_GET_CLASS( id )->conversion_changed( id ); g_signal_emit( G_OBJECT( id ), imagedisplay_signals[SIG_AREA_CHANGED], 0, &conv->canvas ); } /* Part of the repaint has changed. */ static void imagedisplay_conversion_area_changed_cb( Conversion *conv, Rect *dirty, Imagedisplay *id ) { #ifdef DEBUG printf( "imagedisplay_conversion_area_changed_cb: " "left = %d, top = %d, width = %d, height = %d, ", dirty->left, dirty->top, dirty->width, dirty->height ); gobject_print( G_OBJECT( id ) ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( id ), imagedisplay_signals[SIG_AREA_CHANGED], 0, dirty ); } /* Install a conversion. Only allow this once. */ void imagedisplay_set_conversion( Imagedisplay *id, Conversion *conv ) { g_assert( !id->conv ); if( conv ) { id->conv = conv; id->changed_sid = g_signal_connect( id->conv, "changed", G_CALLBACK( imagedisplay_conversion_changed_cb ), id ); id->area_changed_sid = g_signal_connect( id->conv, "area_changed", G_CALLBACK( imagedisplay_conversion_area_changed_cb ), id ); g_object_ref( G_OBJECT( conv ) ); iobject_sink( IOBJECT( conv ) ); /* Trigger a change on the conv so we update. */ iobject_changed( IOBJECT( conv ) ); } } /* Make a new Imagedisplay. Pass in the conversion we should show, conv can * be NULL ... wait for one to be installed. */ Imagedisplay * imagedisplay_new( Conversion *conv ) { Imagedisplay *id = g_object_new( TYPE_IMAGEDISPLAY, NULL ); #ifdef DEBUG g_print( "imagedisplay_new: " ); gobject_print( G_OBJECT( id ) ); #endif /*DEBUG*/ imagedisplay_set_conversion( id, conv ); return( id ); } void imagedisplay_set_shrink_to_fit( Imagedisplay *id, gboolean shrink_to_fit ) { id->shrink_to_fit = shrink_to_fit; if( shrink_to_fit ) conversion_set_mag( id->conv, 0 ); } ================================================ FILE: src/imagedisplay.h ================================================ /* Imagedisplay widget stuff. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IMAGEDISPLAY (imagedisplay_get_type()) #define IMAGEDISPLAY( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IMAGEDISPLAY, Imagedisplay )) #define IMAGEDISPLAY_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_IMAGEDISPLAY, ImagedisplayClass)) #define IS_IMAGEDISPLAY( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IMAGEDISPLAY )) #define IS_IMAGEDISPLAY_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IMAGEDISPLAY )) #define IMAGEDISPLAY_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_IMAGEDISPLAY, ImagedisplayClass )) /* Display an entire image. Put in a scrolled window to see just part of it. */ struct _Imagedisplay { GtkDrawingArea parent_object; /* Image we display. */ Conversion *conv; /* Conversion we display */ guint changed_sid; /* Watch conv with these */ guint area_changed_sid; gboolean shrink_to_fit; /* Auto-shrink mode */ /* GCs also used by region paint. */ GdkGC *back_gc; GdkGC *top_gc; GdkGC *bottom_gc; }; /* Class structure. */ typedef struct _ImagedisplayClass { /* Drawing area we paint in. */ GtkDrawingAreaClass parent_class; /* Virtual methods. */ void (*conversion_changed)( Imagedisplay * ); void (*area_changed)( Imagedisplay *, Rect * ); } ImagedisplayClass; void imagedisplay_queue_draw_area( Imagedisplay *id, Rect *area ); GType imagedisplay_get_type( void ); void imagedisplay_set_conversion( Imagedisplay *id, Conversion *conv ); Imagedisplay *imagedisplay_new( Conversion *conv ); void imagedisplay_set_shrink_to_fit( Imagedisplay *id, gboolean shrink_to_fit ); ================================================ FILE: src/imageheader.c ================================================ /* display an image header */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static iDialogClass *imageheader_parent_class = NULL; /* Our columns. */ enum { NAME_COLUMN, VALUE_COLUMN, N_COLUMNS }; static void imageheader_destroy( GtkObject *object ) { Imageheader *imageheader; g_return_if_fail( object != NULL ); g_return_if_fail( IS_IMAGEHEADER( object ) ); imageheader = IMAGEHEADER( object ); /* My instance destroy stuff. */ UNREF( imageheader->store ); if( GTK_OBJECT_CLASS( imageheader_parent_class )->destroy ) GTK_OBJECT_CLASS( imageheader_parent_class )->destroy( object ); } static void * imageheader_add_item( IMAGE *im, const char *field, GValue *value, Imageheader *imageheader ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); GtkTreeIter iter; /* Show the nicks for enums. */ if( G_VALUE_HOLDS_ENUM( value ) ) vips_buf_appendf( &buf, "%s", vips_enum_nick( G_VALUE_TYPE( value ), g_value_get_enum( value ) ) ); else { char *value_str; value_str = g_strdup_value_contents( value ); vips_buf_appendf( &buf, "%s", value_str ); g_free( value_str ); } gtk_list_store_append( imageheader->store, &iter ); gtk_list_store_set( imageheader->store, &iter, NAME_COLUMN, field, VALUE_COLUMN, vips_buf_all( &buf ), -1 ); return( NULL ); } static void imageheader_refresh( Imageheader *imageheader ) { gtk_list_store_clear( imageheader->store ); if( imageheader->iimage && imageheader->iimage->value.ii ) { Imageinfo *ii = imageheader->iimage->value.ii; IMAGE *im = imageinfo_get( FALSE, ii ); im_header_map( im, (im_header_map_fn) imageheader_add_item, imageheader ); gtk_text_buffer_set_text( gtk_text_view_get_buffer( GTK_TEXT_VIEW( imageheader->history ) ), im_history_get( im ), -1 ); } else { gtk_editable_delete_text( GTK_EDITABLE( imageheader->history ), 0, -1 ); } } static void imageheader_entry_changed_cb( GtkEditable *editable, Imageheader *imageheader ) { gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( imageheader->filter ) ); } static gboolean imageheader_visible_func( GtkTreeModel *model, GtkTreeIter *iter, gpointer data ) { Imageheader *imageheader = IMAGEHEADER( data ); const char *text = gtk_entry_get_text( GTK_ENTRY( imageheader->entry ) ); char *name; char *value; gboolean found; found = FALSE; gtk_tree_model_get( model, iter, NAME_COLUMN, &name, -1 ); if( name ) { found = my_strcasestr( name, text ) != NULL; g_free( name ); } if( found ) return( TRUE ); gtk_tree_model_get( model, iter, VALUE_COLUMN, &value, -1 ); if( value ) { found = my_strcasestr( value, text ) != NULL; g_free( value ); } return( found ); } static void imageheader_build( GtkWidget *widget ) { Imageheader *imageheader = IMAGEHEADER( widget ); iDialog *idlg = IDIALOG( widget ); GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *top; GtkWidget *label; GtkWidget *swin; GtkWidget *pane; GtkWidget *vbox; PangoFontDescription *font_desc; #ifdef DEBUG printf( "imageheader_build: %s\n", IWINDOW( imageheader )->title ); #endif /*DEBUG*/ /* Call all builds in superclasses. */ if( IWINDOW_CLASS( imageheader_parent_class )->build ) (*IWINDOW_CLASS( imageheader_parent_class )->build)( widget ); pane = gtk_vpaned_new(); gtk_box_pack_start( GTK_BOX( idlg->work ), pane, TRUE, TRUE, 2 ); vbox = gtk_vbox_new( FALSE, 2 ); gtk_paned_pack1( GTK_PANED( pane ), vbox, TRUE, FALSE ); top = gtk_hbox_new( FALSE, 12 ); gtk_box_pack_start( GTK_BOX( vbox ), top, FALSE, FALSE, 2 ); imageheader->entry = gtk_entry_new(); gtk_signal_connect( GTK_OBJECT( imageheader->entry ), "changed", GTK_SIGNAL_FUNC( imageheader_entry_changed_cb ), imageheader ); gtk_box_pack_end( GTK_BOX( top ), imageheader->entry, FALSE, FALSE, 2 ); label = gtk_image_new_from_stock( GTK_STOCK_FIND, GTK_ICON_SIZE_MENU ); gtk_box_pack_end( GTK_BOX( top ), label, FALSE, FALSE, 0 ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_start( GTK_BOX( vbox ), swin, TRUE, TRUE, 2 ); imageheader->store = gtk_list_store_new( N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING ); imageheader->filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( imageheader->store ), NULL ); gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER( imageheader->filter ), imageheader_visible_func, imageheader, NULL ); imageheader->tree = gtk_tree_view_new_with_model( GTK_TREE_MODEL( imageheader->filter ) ); gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( imageheader->tree ), TRUE ); gtk_container_add( GTK_CONTAINER( swin ), imageheader->tree ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Field" ), renderer, "text", NAME_COLUMN, NULL ); gtk_tree_view_column_set_resizable( column, TRUE ); gtk_tree_view_append_column( GTK_TREE_VIEW( imageheader->tree ), column ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Value" ), renderer, "text", VALUE_COLUMN, NULL ); gtk_tree_view_column_set_resizable( column, TRUE ); gtk_tree_view_append_column( GTK_TREE_VIEW( imageheader->tree ), column ); vbox = gtk_vbox_new( FALSE, 2 ); gtk_paned_pack2( GTK_PANED( pane ), vbox, TRUE, FALSE ); label = gtk_label_new( _( "Image history" ) ); gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 2 ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_end( GTK_BOX( vbox ), swin, TRUE, TRUE, 2 ); imageheader->history = gtk_text_view_new(); gtk_text_view_set_editable( GTK_TEXT_VIEW( imageheader->history ), FALSE ); gtk_text_view_set_cursor_visible( GTK_TEXT_VIEW( imageheader->history ), FALSE ); font_desc = pango_font_description_from_string( "Monospace" ); gtk_widget_modify_font( imageheader->history, font_desc ); pango_font_description_free( font_desc ); gtk_container_add( GTK_CONTAINER( swin ), imageheader->history ); imageheader_refresh( imageheader ); gtk_window_set_default_size( GTK_WINDOW( imageheader ), 550, 550 ); gtk_paned_set_position( GTK_PANED( pane ), 350 ); gtk_widget_show_all( idlg->work ); } static void imageheader_class_init( ImageheaderClass *class ) { GtkObjectClass *object_class; iWindowClass *iwindow_class; object_class = (GtkObjectClass *) class; iwindow_class = (iWindowClass *) class; object_class->destroy = imageheader_destroy; iwindow_class->build = imageheader_build; imageheader_parent_class = g_type_class_peek_parent( class ); } static void imageheader_init( Imageheader *imageheader ) { #ifdef DEBUG printf( "imageheader_init: %s\n", IWINDOW( imageheader )->title ); #endif /*DEBUG*/ imageheader->iimage = NULL; } GtkType imageheader_get_type( void ) { static GtkType imageheader_type = 0; if( !imageheader_type ) { static const GtkTypeInfo info = { "Imageheader", sizeof( Imageheader ), sizeof( ImageheaderClass ), (GtkClassInitFunc) imageheader_class_init, (GtkObjectInitFunc) imageheader_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; imageheader_type = gtk_type_unique( TYPE_IDIALOG, &info ); } return( imageheader_type ); } /* Conversion has changed signal. */ static void imageheader_ii_changed( Model *model, Imageheader *imageheader ) { g_assert( IS_MODEL( model ) ); g_assert( IS_IMAGEHEADER( imageheader ) ); imageheader_refresh( imageheader ); } static void imageheader_link( Imageheader *imageheader, iImage *iimage ) { imageheader->iimage = iimage; listen_add( G_OBJECT( imageheader ), (GObject **) &imageheader->iimage, "changed", G_CALLBACK( imageheader_ii_changed ) ); } GtkWidget * imageheader_new( iImage *iimage ) { Imageheader *imageheader = gtk_type_new( TYPE_IMAGEHEADER ); imageheader_link( imageheader, iimage ); return( GTK_WIDGET( imageheader ) ); } ================================================ FILE: src/imageheader.h ================================================ /* display an image header */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IMAGEHEADER (imageheader_get_type()) #define IMAGEHEADER( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_IMAGEHEADER, Imageheader )) #define IMAGEHEADER_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IMAGEHEADER, ImageheaderClass )) #define IS_IMAGEHEADER( obj ) (GTK_CHECK_TYPE( (obj), TYPE_IMAGEHEADER )) #define IS_IMAGEHEADER_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IMAGEHEADER )) typedef struct _Imageheader { iDialog parent; iImage *iimage; GtkListStore *store; /* Model for list view */ GtkTreeModel *filter; /* After filtering with search box */ GtkWidget *tree; /* Displayed tree */ GtkWidget *entry; /* Search widget */ GtkWidget *history; } Imageheader; typedef struct _ImageheaderClass { iDialogClass parent_class; /* My methods. */ } ImageheaderClass; GtkType imageheader_get_type( void ); GtkWidget *imageheader_new( iImage *iimage ); ================================================ FILE: src/imageinfo.c ================================================ /* image management ... a layer over the VIPS IMAGE type */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* jobs: - reference counting layer ... in Managed base class, plus links to heap garbage collection - filesystem tracking: we stat open files and signal file_changed if we see a change - cache: several open( "fred.v" )s share a single Imageinfo, provided their mtimes are all the same - lookup table management ... if an operation can work with pixel lookup tables (found by examining a flag in the VIPS function descriptor), then instead of operating on the image, the operation runs on the LUT associated with that image ... Imageinfo tracks the LUTs representing delayed eval - dependency tracking ... an imageinfo can require several other imageinfos to be open for it to work properly; we follow these dependencies, and delay destroying an imageinfo until it's not required by any others - temp file management ... we can make temp images on disc; we unlink() these temps when they're no longer needed - imageinfo/expr association tracking ... we track when an expr receives an imageinfo as its value; the info is used to get region views to display in the right image ... see expr_real_new_value() - paint stuff: also undo/redo buffers, each with a "*_changed" signal */ /* more stuff: while we transition to vips8, also use imageinfo to wrap VipsImage most of the jobs above are pushed down into vips8 now ... except for - reference counting layer ... in Managed base class - filesystem tracking: we stat open files and signal file_changed if we see a change - cache: several open( "fred.v" )s share a single Imageinfo, provided their mtimes are all the same */ #include "ip.h" /* #define DEBUG #define DEBUG_MAKE #define DEBUG_RGB #define DEBUG_OPEN #define DEBUG_CHECK */ static iContainerClass *imageinfogroup_parent_class = NULL; static void imageinfogroup_finalize( GObject *gobject ) { Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( gobject ); IM_FREEF( g_hash_table_destroy, imageinfogroup->filename_hash ); G_OBJECT_CLASS( imageinfogroup_parent_class )->finalize( gobject ); } static void imageinfogroup_child_add( iContainer *parent, iContainer *child, int pos ) { Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( parent ); Imageinfo *imageinfo = IMAGEINFO( child ); const char *name = IOBJECT( imageinfo )->name; GSList *hits; hits = (GSList *) g_hash_table_lookup( imageinfogroup->filename_hash, name ); hits = g_slist_prepend( hits, imageinfo ); g_hash_table_insert( imageinfogroup->filename_hash, (gpointer) name, (gpointer) hits ); ICONTAINER_CLASS( imageinfogroup_parent_class )-> child_add( parent, child, pos ); } static void imageinfogroup_child_remove( iContainer *parent, iContainer *child ) { Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( parent ); Imageinfo *imageinfo = IMAGEINFO( child ); const char *name = IOBJECT( imageinfo )->name; GSList *hits; hits = (GSList *) g_hash_table_lookup( imageinfogroup->filename_hash, name ); g_assert( hits ); hits = g_slist_remove( hits, imageinfo ); /* child is going away (probably), so we don't want to link hits back * on again with child->name as the key ... if possible, look down * hits for another name we can use instead. */ if( hits ) { const char *new_name = IOBJECT( hits->data )->name; g_hash_table_replace( imageinfogroup->filename_hash, (gpointer) new_name, (gpointer) hits ); } else g_hash_table_remove( imageinfogroup->filename_hash, (gpointer) name ); ICONTAINER_CLASS( imageinfogroup_parent_class )-> child_remove( parent, child ); } static void imageinfogroup_class_init( ImageinfogroupClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iContainerClass *icontainer_class = ICONTAINER_CLASS( class ); imageinfogroup_parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = imageinfogroup_finalize; icontainer_class->child_add = imageinfogroup_child_add; icontainer_class->child_remove = imageinfogroup_child_remove; } static void imageinfogroup_init( Imageinfogroup *imageinfogroup ) { #ifdef DEBUG printf( "imageinfogroup_init\n" ); #endif /*DEBUG*/ imageinfogroup->filename_hash = g_hash_table_new( g_str_hash, g_str_equal ); } GType imageinfogroup_get_type( void ) { static GType imageinfogroup_type = 0; if( !imageinfogroup_type ) { static const GTypeInfo info = { sizeof( ImageinfogroupClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) imageinfogroup_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Imageinfogroup ), 32, /* n_preallocs */ (GInstanceInitFunc) imageinfogroup_init, }; imageinfogroup_type = g_type_register_static( TYPE_ICONTAINER, "Imageinfogroup", &info, 0 ); } return( imageinfogroup_type ); } Imageinfogroup * imageinfogroup_new( void ) { Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( g_object_new( TYPE_IMAGEINFOGROUP, NULL ) ); return( imageinfogroup ); } static void * imageinfogroup_lookup_test( Imageinfo *imageinfo, struct stat *buf ) { const char *name = IOBJECT( imageinfo )->name; if( name && buf->st_mtime == imageinfo->mtime ) return( imageinfo ); return( NULL ); } /* Look up by filename ... mtimes have to match too. */ static Imageinfo * imageinfogroup_lookup( Imageinfogroup *imageinfogroup, const char *filename ) { GSList *hits; Imageinfo *imageinfo; struct stat buf; if( stat( filename, &buf ) == 0 && (hits = (GSList *) g_hash_table_lookup( imageinfogroup->filename_hash, filename )) && (imageinfo = IMAGEINFO( slist_map( hits, (SListMapFn) imageinfogroup_lookup_test, &buf ) )) ) return( imageinfo ); return( NULL ); } /* Our signals. */ enum { SIG_AREA_CHANGED, /* Area of image has changed: update screen */ SIG_AREA_PAINTED, /* Area of image has been painted */ SIG_UNDO_CHANGED, /* Undo/redo state has changed */ SIG_FILE_CHANGED, /* Underlying file seems to have changed */ SIG_INVALIDATE, /* IMAGE* has been invalidated */ SIG_LAST }; static ManagedClass *parent_class = NULL; static guint imageinfo_signals[SIG_LAST] = { 0 }; #if defined(DEBUG) || defined(DEBUG_OPEN) || defined(DEBUG_RGB) || \ defined(DEBUG_CHECK) || defined(DEBUG_MAKE) static void imageinfo_print( Imageinfo *imageinfo ) { printf( " \"%s\" mtime = %d (%p)\n", IOBJECT( imageinfo )->name, (int) imageinfo->mtime, imageinfo ); } #endif void * imageinfo_area_changed( Imageinfo *imageinfo, Rect *dirty ) { g_assert( IS_IMAGEINFO( imageinfo ) ); #ifdef DEBUG printf( "imageinfo_area_changed: " "left = %d, top = %d, width = %d, height = %d\n", dirty->left, dirty->top, dirty->width, dirty->height ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( imageinfo ), imageinfo_signals[SIG_AREA_CHANGED], 0, dirty ); return( NULL ); } void * imageinfo_area_painted( Imageinfo *imageinfo, Rect *dirty ) { g_assert( IS_IMAGEINFO( imageinfo ) ); #ifdef DEBUG printf( "imageinfo_area_painted: left = %d, top = %d, " "width = %d, height = %d\n", dirty->left, dirty->top, dirty->width, dirty->height ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( imageinfo ), imageinfo_signals[SIG_AREA_PAINTED], 0, dirty ); return( NULL ); } static void * imageinfo_undo_changed( Imageinfo *imageinfo ) { g_assert( IS_IMAGEINFO( imageinfo ) ); g_signal_emit( G_OBJECT( imageinfo ), imageinfo_signals[SIG_UNDO_CHANGED], 0 ); return( NULL ); } static void * imageinfo_file_changed( Imageinfo *imageinfo ) { g_assert( IS_IMAGEINFO( imageinfo ) ); #ifdef DEBUG_CHECK printf( "imageinfo_file_changed:" ); imageinfo_print( imageinfo ); #endif /*DEBUG_CHECK*/ g_signal_emit( G_OBJECT( imageinfo ), imageinfo_signals[SIG_FILE_CHANGED], 0 ); return( NULL ); } static void * imageinfo_invalidate( Imageinfo *imageinfo ) { g_assert( IS_IMAGEINFO( imageinfo ) ); #ifdef DEBUG_CHECK printf( "imageinfo_invalidate:" ); imageinfo_print( imageinfo ); #endif /*DEBUG_CHECK*/ g_signal_emit( G_OBJECT( imageinfo ), imageinfo_signals[SIG_INVALIDATE], 0 ); return( NULL ); } void imageinfo_expr_add( Imageinfo *imageinfo, Expr *expr ) { #ifdef DEBUG printf( "imageinfo_expr_add: " ); expr_name_print( expr ); printf( "has imageinfo \"%s\" as value\n", imageinfo->im->filename ); #endif /*DEBUG*/ g_assert( !g_slist_find( imageinfo->exprs, expr ) ); g_assert( !expr->imageinfo ); expr->imageinfo = imageinfo; imageinfo->exprs = g_slist_prepend( imageinfo->exprs, expr ); } void * imageinfo_expr_remove( Expr *expr, Imageinfo *imageinfo ) { #ifdef DEBUG printf( "imageinfo_expr_remove: " ); expr_name_print( expr ); printf( "has lost imageinfo \"%s\" as value\n", imageinfo->im->filename ); #endif /*DEBUG*/ g_assert( expr->imageinfo ); g_assert( g_slist_find( imageinfo->exprs, expr ) ); g_assert( expr->imageinfo == imageinfo ); expr->imageinfo = NULL; imageinfo->exprs = g_slist_remove( imageinfo->exprs, expr ); return( NULL ); } GSList * imageinfo_expr_which( Imageinfo *imageinfo ) { return( imageinfo->exprs ); } /* Find the underlying image in an imageinfo. */ IMAGE * imageinfo_get_underlying( Imageinfo *imageinfo ) { if( imageinfo->underlying ) return( imageinfo_get_underlying( imageinfo->underlying ) ); else return( imageinfo->im ); } /* Free up an undo fragment. */ static void imageinfo_undofragment_free( Undofragment *frag ) { IM_FREEF( im_close, frag->im ); IM_FREE( frag ); } /* Free an undo buffer. */ static void imageinfo_undobuffer_free( Undobuffer *undo ) { slist_map( undo->frags, (SListMapFn) imageinfo_undofragment_free, NULL ); IM_FREEF( g_slist_free, undo->frags ); IM_FREE( undo ); } /* Free all undo information attached to an imageinfo. */ static void imageinfo_undo_free( Imageinfo *imageinfo ) { slist_map( imageinfo->redo, (SListMapFn) imageinfo_undobuffer_free, NULL ); IM_FREEF( g_slist_free, imageinfo->redo ); slist_map( imageinfo->undo, (SListMapFn) imageinfo_undobuffer_free, NULL ); IM_FREEF( g_slist_free, imageinfo->undo ); IM_FREEF( imageinfo_undobuffer_free, imageinfo->cundo ); } static void imageinfo_dispose_eval( Imageinfo *imageinfo ) { imageinfo->monitored = FALSE; /* Make sure any callbacks from the IMAGE stop working. */ if( imageinfo->proxy ) { imageinfo->proxy->imageinfo = NULL; imageinfo->proxy = NULL; } } static void imageinfo_dispose( GObject *gobject ) { Imageinfo *imageinfo = IMAGEINFO( gobject ); #ifdef DEBUG_OPEN printf( "imageinfo_dispose:" ); imageinfo_print( imageinfo ); #endif /*DEBUG_OPEN*/ slist_map( imageinfo->exprs, (SListMapFn) imageinfo_expr_remove, imageinfo ); g_assert( !imageinfo->exprs ); imageinfo_dispose_eval( imageinfo ); IM_FREEF( g_source_remove, imageinfo->check_tid ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } /* Final death! */ static void imageinfo_finalize( GObject *gobject ) { Imageinfo *imageinfo = IMAGEINFO( gobject ); gboolean isfile = imageinfo->im && im_isfile( imageinfo->im ); #ifdef DEBUG_MAKE printf( "imageinfo_finalize:" ); imageinfo_print( imageinfo ); #endif /*DEBUG_MAKE*/ IM_FREEF( im_close, imageinfo->im ); IM_FREEF( im_close, imageinfo->mapped_im ); IM_FREEF( im_close, imageinfo->identity_lut ); if( imageinfo->dfile && imageinfo->delete_filename && isfile ) { #ifdef DEBUG_OPEN printf( "imageinfo_destroy: unlinking \"%s\"\n", name ); #endif /*DEBUG_OPEN*/ unlinkf( "%s", imageinfo->delete_filename ); iobject_changed( IOBJECT( main_imageinfogroup ) ); } VIPS_FREE( imageinfo->delete_filename ); MANAGED_UNREF( imageinfo->underlying ); imageinfo_undo_free( imageinfo ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Make an info string about an imageinfo. */ static void imageinfo_info( iObject *iobject, VipsBuf *buf ) { Imageinfo *imageinfo = IMAGEINFO( iobject ); vips_buf_appendi( buf, imageinfo_get( FALSE, imageinfo ) ); /* Don't chain up to parent->info(), we don't want all the other * stuff, this is going to be used for a caption. */ } static void imageinfo_real_area_changed( Imageinfo *imageinfo, Rect *dirty ) { } static void imageinfo_real_area_painted( Imageinfo *imageinfo, Rect *dirty ) { /* Cache attaches to this signal and invalidates on paint. Trigger a * repaint in turn. */ imageinfo_area_changed( imageinfo, dirty ); } static void imageinfo_real_undo_changed( Imageinfo *imageinfo ) { } static void imageinfo_real_file_changed( Imageinfo *imageinfo ) { } static void imageinfo_real_invalidate( Imageinfo *imageinfo ) { } static void imageinfo_class_init( ImageinfoClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); ManagedClass *managed_class = MANAGED_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = imageinfo_dispose; gobject_class->finalize = imageinfo_finalize; iobject_class->info = imageinfo_info; /* Timeout on unreffed images. */ managed_class->keepalive = 60.0; class->area_changed = imageinfo_real_area_changed; class->area_painted = imageinfo_real_area_painted; class->undo_changed = imageinfo_real_undo_changed; class->file_changed = imageinfo_real_file_changed; class->invalidate = imageinfo_real_invalidate; /* Create signals. */ imageinfo_signals[SIG_AREA_CHANGED] = g_signal_new( "area_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ImageinfoClass, area_changed ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER ); imageinfo_signals[SIG_AREA_PAINTED] = g_signal_new( "area_painted", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ImageinfoClass, area_painted ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER ); imageinfo_signals[SIG_UNDO_CHANGED] = g_signal_new( "undo_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ImageinfoClass, undo_changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); imageinfo_signals[SIG_FILE_CHANGED] = g_signal_new( "file_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ImageinfoClass, file_changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); imageinfo_signals[SIG_INVALIDATE] = g_signal_new( "invalidate", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ImageinfoClass, invalidate ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } static void imageinfo_init( Imageinfo *imageinfo ) { #ifdef DEBUG_MAKE printf( "imageinfo_init: %p\n", imageinfo ); #endif /*DEBUG_MAKE*/ imageinfo->im = NULL; imageinfo->mapped_im = NULL; imageinfo->identity_lut = NULL; imageinfo->underlying = NULL; imageinfo->proxy = NULL; imageinfo->dfile = FALSE; imageinfo->delete_filename = NULL; imageinfo->from_file = FALSE; imageinfo->mtime = 0; imageinfo->exprs = NULL; imageinfo->ok_to_paint = FALSE; imageinfo->undo = NULL; imageinfo->redo = NULL; imageinfo->cundo = NULL; imageinfo->monitored = FALSE; imageinfo->check_mtime = 0; imageinfo->check_tid = 0; } GType imageinfo_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ImageinfoClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) imageinfo_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Imageinfo ), 32, /* n_preallocs */ (GInstanceInitFunc) imageinfo_init, }; type = g_type_register_static( TYPE_MANAGED, "Imageinfo", &info, 0 ); } return( type ); } static int imageinfo_proxy_eval( Imageinfoproxy *proxy ) { Imageinfo *imageinfo = proxy->imageinfo; if( imageinfo && imageinfo->im->time ) if( progress_update_percent( imageinfo->im->time->percent, imageinfo->im->time->eta ) ) return( -1 ); return( 0 ); } static int imageinfo_proxy_invalidate( Imageinfoproxy *proxy ) { Imageinfo *imageinfo = proxy->imageinfo; if( imageinfo ) imageinfo_invalidate( imageinfo ); return( 0 ); } static int imageinfo_proxy_preclose( Imageinfoproxy *proxy ) { Imageinfo *imageinfo = proxy->imageinfo; /* Remove everything related to progress. */ if( imageinfo ) imageinfo_dispose_eval( imageinfo ); return( 0 ); } /* Add a proxy to track IMAGE events. */ static void imageinfo_proxy_add( Imageinfo *imageinfo ) { /* Only if we're running interactively. */ if( main_option_batch ) return; /* Already being monitored? */ if( imageinfo->monitored ) return; imageinfo->monitored = TRUE; /* Need a proxy on IMAGE. */ g_assert( !imageinfo->proxy ); if( !(imageinfo->proxy = IM_NEW( imageinfo->im, Imageinfoproxy )) ) return; imageinfo->proxy->im = imageinfo->im; imageinfo->proxy->imageinfo = imageinfo; (void) im_add_eval_callback( imageinfo->im, (im_callback_fn) imageinfo_proxy_eval, imageinfo->proxy, NULL ); (void) im_add_invalidate_callback( imageinfo->im, (im_callback_fn) imageinfo_proxy_invalidate, imageinfo->proxy, NULL ); /* Has to be preclose, because we want to be sure we disconnect before * the proxy is freed on a close callback. */ (void) im_add_preclose_callback( imageinfo->im, (im_callback_fn) imageinfo_proxy_preclose, imageinfo->proxy, NULL ); } /* Make a basic imageinfo. No refs, will be destroyed on next GC. If name is * NULL, make a temp name up; otherwise name needs to be unique. */ Imageinfo * imageinfo_new( Imageinfogroup *imageinfogroup, Heap *heap, IMAGE *im, const char *name ) { Imageinfo *imageinfo = IMAGEINFO( g_object_new( TYPE_IMAGEINFO, NULL ) ); char buf[FILENAME_MAX]; #ifdef DEBUG_OPEN printf( "imageinfo_new: %p \"%s\"\n", imageinfo, im->filename ); #endif /*DEBUG_OPEN*/ managed_link_heap( MANAGED( imageinfo ), heap ); if( !name ) { if( !temp_name( buf, "v" ) ) /* Will be freed on next GC. */ return( NULL ); name = buf; } iobject_set( IOBJECT( imageinfo ), name, NULL ); /* Only record the pointer when we know we will make the imageinfo * successfully. */ imageinfo->im = im; icontainer_child_add( ICONTAINER( imageinfogroup ), ICONTAINER( imageinfo ), -1 ); imageinfo_proxy_add( imageinfo ); return( imageinfo ); } /* An image is a result of a LUT operation on an earlier imageinfo. */ void imageinfo_set_underlying( Imageinfo *top_imageinfo, Imageinfo *imageinfo ) { g_assert( !top_imageinfo->underlying ); top_imageinfo->underlying = imageinfo; MANAGED_REF( top_imageinfo->underlying ); } /* Make a temp image. Deleted on close. No refs: closed on next GC. If you * want it to stick around, ref it! */ Imageinfo * imageinfo_new_temp( Imageinfogroup *imageinfogroup, Heap *heap, const char *name, const char *mode ) { IMAGE *im; char tname[FILENAME_MAX]; Imageinfo *imageinfo; if( !temp_name( tname, "v" ) || !(im = im_open( tname, mode )) ) return( NULL ); if( !(imageinfo = imageinfo_new( imageinfogroup, heap, im, name )) ) { im_close( im ); return( NULL ); } imageinfo->dfile = TRUE; VIPS_SETSTR( imageinfo->delete_filename, tname ); return( imageinfo ); } /* Need this context during imageinfo_open_image_input(). */ typedef struct _ImageinfoOpen { Imageinfogroup *imageinfogroup; Heap *heap; const char *filename; GtkWidget *parent; } ImageinfoOpen; /* Open for read ... returns a non-heap pointer, destroy if it goes in the * heap. */ static Imageinfo * imageinfo_open_image_input( const char *filename, ImageinfoOpen *open ) { Imageinfo *imageinfo; VipsFormatClass *format; if( !(format = vips_format_for_file( filename )) ) return( NULL ); if( strcmp( VIPS_OBJECT_CLASS( format )->nickname, "vips" ) == 0 ) { IMAGE *im; if( !(im = im_open( filename, "r" )) ) return( NULL ); if( !(imageinfo = imageinfo_new( open->imageinfogroup, open->heap, im, open->filename )) ) { im_close( im ); return( NULL ); } MANAGED_REF( imageinfo ); #ifdef DEBUG_OPEN printf( "imageinfo_open_image_input: opened VIPS \"%s\"\n", filename ); #endif /*DEBUG_OPEN*/ } else { VipsFormatFlags flags = vips_format_get_flags( format, filename ); const char *mode = flags & VIPS_FORMAT_PARTIAL ? "p" : "w"; if( !(imageinfo = imageinfo_new_temp( open->imageinfogroup, open->heap, open->filename, mode )) ) return( NULL ); MANAGED_REF( imageinfo ); if( format->load( filename, imageinfo->im ) || im_histlin( imageinfo->im, "im_copy %s %s", filename, imageinfo->im->filename ) ) { MANAGED_UNREF( imageinfo ); return( NULL ); } #ifdef DEBUG_OPEN printf( "imageinfo_open_image_input: opened %s \"%s\"\n", VIPS_OBJECT_CLASS( format )->nickname, filename ); #endif /*DEBUG_OPEN*/ } /* Get ready for input. */ if( im_pincheck( imageinfo->im ) ) return( NULL ); /* The rewind will have removed everything from the IMAGE. Reattach * progress. */ imageinfo_proxy_add( imageinfo ); /* Attach the original filename ... pick this up again later as a * save default. */ if( im_meta_set_string( imageinfo->im, ORIGINAL_FILENAME, filename ) ) return( NULL ); return( imageinfo ); } Imageinfo * imageinfo_new_from_pixbuf( Imageinfogroup *imageinfogroup, Heap *heap, GdkPixbuf *pixbuf ) { int width; int height; int bands; guchar *bytes; Imageinfo *ii; size_t vips_length; width = gdk_pixbuf_get_width( pixbuf ); height = gdk_pixbuf_get_height( pixbuf ); bands = gdk_pixbuf_get_n_channels( pixbuf ); /* 2.26 and later have gdk_pixbuf_get_pixels_with_length() * which would let us check the size, but we can't reslly use it yet. * Another time! guint length; bytes = gdk_pixbuf_get_pixels_with_length( pixbuf, &length ); if( vips_length != length ) { error_top( _( "Unable to create image." ) ); error_sub( _( "vips expected %zd bytes, gdkpixbuf made %d" ), vips_length, length ); return( NULL ); } */ bytes = gdk_pixbuf_get_pixels( pixbuf ); if( !(ii = imageinfo_new_temp( imageinfogroup, heap, NULL, "t" )) ) return( NULL ); im_initdesc( ii->im, width, height, bands, IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, IM_TYPE_sRGB, 1.0, 1.0, 0, 0 ); if( im_setupout( ii->im ) ) return( NULL ); vips_length = VIPS_IMAGE_SIZEOF_LINE( ii->im ) * height; memcpy( ii->im->data, bytes, vips_length ); return( ii ); } /* Was this ii loaded from a file (ie. ->name contains a filename the user * might recognise). */ gboolean imageinfo_is_from_file( Imageinfo *imageinfo ) { return( IOBJECT( imageinfo )->name && imageinfo->from_file ); } static gint imageinfo_attach_check_cb( Imageinfo *imageinfo ) { if( imageinfo_is_from_file( imageinfo ) && imageinfo->check_tid ) { struct stat buf; if( !stat( IOBJECT( imageinfo )->name, &buf ) && buf.st_mtime != imageinfo->check_mtime ) { imageinfo->check_mtime = buf.st_mtime; imageinfo_file_changed( imageinfo ); } } return( TRUE ); } /* Start checking this file for updates, signal reload if there is one. */ static void imageinfo_attach_check( Imageinfo *imageinfo ) { if( imageinfo_is_from_file( imageinfo ) && !imageinfo->check_tid ) { struct stat buf; /* Need to be able to stat() to be able to track a file. */ if( stat( IOBJECT( imageinfo )->name, &buf ) ) return; imageinfo->mtime = buf.st_mtime; imageinfo->check_mtime = imageinfo->mtime; imageinfo->check_tid = g_timeout_add( 1000, (GSourceFunc) imageinfo_attach_check_cb, imageinfo ); #ifdef DEBUG_CHECK printf( "imageinfo_attach_check: starting to check" ); imageinfo_print( imageinfo ); #endif /*DEBUG_CHECK*/ } else IM_FREEF( g_source_remove, imageinfo->check_tid ); } /* Open a filename for input. The filenmae can have an embedded mode. */ Imageinfo * imageinfo_new_input( Imageinfogroup *imageinfogroup, GtkWidget *parent, Heap *heap, const char *name ) { Imageinfo *imageinfo; ImageinfoOpen open; if( (imageinfo = imageinfogroup_lookup( imageinfogroup, name )) ) { /* We always make a new non-heap pointer. */ MANAGED_REF( imageinfo ); return( imageinfo ); } open.imageinfogroup = imageinfogroup; open.heap = heap; open.filename = name; open.parent = parent; if( !(imageinfo = (Imageinfo *) callv_string_filename( (callv_string_fn) imageinfo_open_image_input, name, &open, NULL, NULL )) ) { error_top( _( "Unable to open image." ) ); error_sub( _( "Unable to open file \"%s\" as image." ), name ); error_vips(); return( NULL ); } imageinfo->from_file = TRUE; imageinfo_attach_check( imageinfo ); return( imageinfo ); } /* Add an identity lut, if this is a LUTtable image. */ static IMAGE * imageinfo_get_identity_lut( Imageinfo *imageinfo ) { if( imageinfo->im->Coding == IM_CODING_NONE && imageinfo->im->BandFmt == IM_BANDFMT_UCHAR ) { if( !imageinfo->identity_lut ) { char tname[FILENAME_MAX]; IMAGE *im; if( !temp_name( tname, "v" ) || !(im = im_open( tname, "p" )) ) return( NULL ); imageinfo->identity_lut = im; if( im_identity( imageinfo->identity_lut, imageinfo->im->Bands ) || im_histlin( imageinfo->identity_lut, "im_identity %s %d", imageinfo->identity_lut->filename, imageinfo->im->Bands ) ) return( NULL ); } return( imageinfo->identity_lut ); } else return( NULL ); } static IMAGE * imageinfo_get_mapped( Imageinfo *imageinfo ) { if( !imageinfo->mapped_im ) { IMAGE *im = imageinfo_get_underlying( imageinfo ); IMAGE *mapped_im; char name[FILENAME_MAX]; char *argv[4]; if( !temp_name( name, "v" ) || !(mapped_im = im_open( name, "p" )) ) return( NULL ); argv[0] = im->filename; argv[1] = mapped_im->filename; argv[2] = imageinfo->im->filename; argv[3] = NULL; if( im_maplut( im, mapped_im, imageinfo->im ) || im_updatehist( mapped_im, "im_maplut", 3, argv ) ) { im_close( mapped_im ); error_vips_all(); return( NULL ); } imageinfo->mapped_im = mapped_im; } return( imageinfo->mapped_im ); } /* Get a lut ... or not! */ IMAGE * imageinfo_get( gboolean use_lut, Imageinfo *imageinfo ) { if( !imageinfo ) return( NULL ); if( use_lut && imageinfo->underlying ) return( imageinfo->im ); if( use_lut && !imageinfo->underlying ) { IMAGE *lut; if( (lut = imageinfo_get_identity_lut( imageinfo )) ) return( lut ); else return( imageinfo->im ); } else if( !use_lut && imageinfo->underlying ) return( imageinfo_get_mapped( imageinfo ) ); else return( imageinfo->im ); } /* Do a set of II all refer to the same underlying image? Used to spot * LUTable optimisations. */ gboolean imageinfo_same_underlying( Imageinfo *imageinfo[], int n ) { int i; if( n < 2 ) return( TRUE ); else { IMAGE *first = imageinfo_get_underlying( imageinfo[0] ); for( i = 1; i < n; i++ ) if( imageinfo_get_underlying( imageinfo[i] ) != first ) return( FALSE ); return( TRUE ); } } /* Write to a filename. */ gboolean imageinfo_write( Imageinfo *imageinfo, const char *name ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); if( vips_format_write( im, name ) ) { char filename[FILENAME_MAX]; char mode[FILENAME_MAX]; im_filename_split( name, filename, mode ); error_top( _( "Unable to write to file." ) ); error_sub( _( "Error writing image to file \"%s\"." ), filename ); error_vips(); return( FALSE ); } return( TRUE ); } static gboolean imageinfo_make_paintable( Imageinfo *imageinfo ) { progress_begin(); if( im_rwcheck( imageinfo->im ) ) { progress_end(); error_top( _( "Unable to paint on image." ) ); error_sub( _( "Unable to get write permission for " "file \"%s\".\nCheck permission settings." ), imageinfo->im->filename ); error_vips(); return( FALSE ); } progress_end(); imageinfo->ok_to_paint = TRUE; return( TRUE ); } static void imageinfo_check_paintable_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Imageinfo *imageinfo = IMAGEINFO( client ); if( !imageinfo_make_paintable( imageinfo ) ) { nfn( sys, IWINDOW_ERROR ); return; } nfn( sys, IWINDOW_YES ); } /* Check painting is OK. nfn() called on "ok!". Returns FALSE if it's * not immediately obvious that we can paint. */ gboolean imageinfo_check_paintable( Imageinfo *imageinfo, GtkWidget *parent, iWindowNotifyFn nfn, void *sys ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); if( im && im_isfile( im ) && !imageinfo->dfile && !imageinfo->ok_to_paint ) { iDialog *idlg; idlg = box_yesno( parent, imageinfo_check_paintable_cb, iwindow_true_cb, imageinfo, nfn, sys, _( "Modify" ), _( "Modify disc file?" ), _( "This image is being shown directly from the " "disc file:\n\n" " %s\n\n" "If you paint on this file, it will be permanently " "changed. If something goes wrong, you may lose work. " "Are you sure you want to modify this file?" ), IOBJECT( imageinfo )->name ); idialog_set_iobject( idlg, IOBJECT( imageinfo ) ); return( FALSE ); } else if( im && !im_isfile( im ) && !imageinfo->ok_to_paint ) { if( !imageinfo_make_paintable( imageinfo ) ) { nfn( sys, IWINDOW_ERROR ); return( FALSE ); } } nfn( sys, IWINDOW_YES ); return( TRUE ); } /* Try to get an Imageinfo from a symbol. */ Imageinfo * imageinfo_sym_image( Symbol *sym ) { PElement *root = &sym->expr->root; if( sym->type == SYM_VALUE && PEISIMAGE( root ) ) return( PEGETII( root ) ); else return( NULL ); } static Undofragment * imageinfo_undofragment_new( Undobuffer *undo ) { Undofragment *frag = INEW( NULL, Undofragment ); frag->undo = undo; frag->im = NULL; return( frag ); } static Undobuffer * imageinfo_undobuffer_new( Imageinfo *imageinfo ) { Undobuffer *undo = INEW( NULL, Undobuffer ); undo->imageinfo = imageinfo; undo->frags = NULL; /* No pixels in bounding box at the moment. */ undo->bbox.left = 0; undo->bbox.top = 0; undo->bbox.width = 0; undo->bbox.height = 0; return( undo ); } /* Grab from the image into an IMAGE buffer. Always grab to memory. */ static IMAGE * imageinfo_undo_grab_area( IMAGE *im, Rect *dirty ) { IMAGE *save; /* Make new image to extract to. */ if( !(save = im_open( "undo buffer", "t" )) ) return( NULL ); /* Try to extract from im. */ if( im_extract_area( im, save, dirty->left, dirty->top, dirty->width, dirty->height ) ) { im_close( save ); error_vips_all(); return( NULL ); } return( save ); } /* Grab into an undo fragment. Add frag to frag list on undo buffer, expand * bounding box. */ static Undofragment * imageinfo_undo_grab( Undobuffer *undo, Rect *dirty ) { Imageinfo *imageinfo = undo->imageinfo; Undofragment *frag = imageinfo_undofragment_new( undo ); IMAGE *im = imageinfo_get( FALSE, imageinfo ); Rect bbox; /* Try to extract from im. Memory allocation happens at this * point, so we must be careful! */ if( !(frag->im = imageinfo_undo_grab_area( im, dirty )) ) { imageinfo_undofragment_free( frag ); error_vips_all(); return( NULL ); } /* Note position of this frag. */ frag->pos = *dirty; /* Add frag to frag list on undo buffer. */ undo->frags = g_slist_prepend( undo->frags, frag ); /* Find bounding box for saved pixels. */ im_rect_unionrect( dirty, &undo->bbox, &bbox ); undo->bbox = bbox; /* Return new frag. */ return( frag ); } /* Trim the undo buffer if we have more than x items on it. */ static void imageinfo_undo_trim( Imageinfo *imageinfo ) { int max = PAINTBOX_MAX_UNDO; int len = g_slist_length( imageinfo->undo ); if( max >= 0 && len > max ) { GSList *l; int i; l = g_slist_reverse( imageinfo->undo ); for( i = 0; i < len - max; i++ ) { Undobuffer *undo = (Undobuffer *) l->data; imageinfo_undobuffer_free( undo ); l = g_slist_remove( l, undo ); } imageinfo->undo = g_slist_reverse( l ); } #ifdef DEBUG printf( "imageinfo_undo_trim: %d items in undo buffer\n", g_slist_length( imageinfo->undo ) ); #endif /*DEBUG*/ } /* Mark the start or end of an undo session. Copy current undo information * to the undo buffers and NULL out the current undo pointer. Junk all redo * information: this new undo action makes all that out of date. */ void imageinfo_undo_mark( Imageinfo *imageinfo ) { /* Is there an existing undo save area? */ if( imageinfo->cundo ) { /* Left over from the last undo save. Copy to undo save list * and get ready for new undo buffer. */ imageinfo->undo = g_slist_prepend( imageinfo->undo, imageinfo->cundo ); imageinfo->cundo = NULL; } /* Junk all redo information. */ slist_map( imageinfo->redo, (SListMapFn) imageinfo_undobuffer_free, NULL ); IM_FREEF( g_slist_free, imageinfo->redo ); /* Trim undo buffer. */ imageinfo_undo_trim( imageinfo ); /* Update menus. */ imageinfo_undo_changed( imageinfo ); } /* Add to the undo buffer. If there is no undo buffer currently under * construction, make a new one. If there is an existing undo buffer, try to * grow it left/right/up/down so as to just enclose the new bounding box. We * assume that our dirty areas are not going to be disconnected. Is this * always true? No - if you move smudge or smear quickly, you can get * non-overlapping areas. However: if you do lots of little operations in more * or less the same place (surely the usual case), then this technique will be * far better. */ static gboolean imageinfo_undo_add( Imageinfo *imageinfo, Rect *dirty ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); Undobuffer *undo = imageinfo->cundo; Rect over, image, clipped; /* Undo disabled? Do nothing. */ if( PAINTBOX_MAX_UNDO == 0 ) return( TRUE ); /* Clip dirty against image size. */ image.left = 0; image.top = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_intersectrect( &image, dirty, &clipped ); /* Is there anything left? If not, can return immediately. */ if( im_rect_isempty( &clipped ) ) return( TRUE ); if( !undo ) { /* No current undo buffer ... start a new one for this action. */ if( !(imageinfo->cundo = undo = imageinfo_undobuffer_new( imageinfo )) ) return( FALSE ); return( imageinfo_undo_grab( undo, &clipped ) != NULL ); } /* Existing stuff we are to add to. Try to expand our undo * area to just enclose the new bounding box. We assume that * there is an overlap between the new and old stuff. */ /* Do we need to expand our saved area to the right? */ if( IM_RECT_RIGHT( &clipped ) > IM_RECT_RIGHT( &undo->bbox ) ) { /* Expand to the right. Calculate the section we need * to add to our bounding box. */ over.left = IM_RECT_RIGHT( &undo->bbox ); over.top = undo->bbox.top; over.width = IM_RECT_RIGHT( &clipped ) - IM_RECT_RIGHT( &undo->bbox ); over.height = undo->bbox.height; /* Grab new fragment. */ if( !imageinfo_undo_grab( undo, &over ) ) return( FALSE ); } /* Do we need to expand our saved area to the left? */ if( undo->bbox.left > clipped.left ) { over.left = clipped.left; over.top = undo->bbox.top; over.width = undo->bbox.left - clipped.left; over.height = undo->bbox.height; if( !imageinfo_undo_grab( undo, &over ) ) return( FALSE ); } /* Do we need to expand our saved area upwards? */ if( undo->bbox.top > clipped.top ) { over.left = undo->bbox.left; over.top = clipped.top; over.width = undo->bbox.width; over.height = undo->bbox.top - clipped.top; if( !imageinfo_undo_grab( undo, &over ) ) return( FALSE ); } /* Do we need to expand our saved area downwards? */ if( IM_RECT_BOTTOM( &clipped ) > IM_RECT_BOTTOM( &undo->bbox ) ) { over.left = undo->bbox.left; over.top = IM_RECT_BOTTOM( &undo->bbox ); over.width = undo->bbox.width; over.height = IM_RECT_BOTTOM( &clipped ) - IM_RECT_BOTTOM( &undo->bbox ); if( !imageinfo_undo_grab( undo, &over ) ) return( FALSE ); } return( TRUE ); } /* Paste an undo fragment back into the image. */ static void * imageinfo_undofragment_paste( Undofragment *frag ) { Undobuffer *undo = frag->undo; Imageinfo *imageinfo = undo->imageinfo; IMAGE *im = imageinfo_get( FALSE, imageinfo ); im_insertplace( im, frag->im, frag->pos.left, frag->pos.top ); imageinfo_area_painted( imageinfo, &frag->pos ); return( NULL ); } /* Paste a whole undo buffer back into the image. */ static void imageinfo_undobuffer_paste( Undobuffer *undo ) { slist_map( undo->frags, (SListMapFn) imageinfo_undofragment_paste, NULL ); } /* Undo a paint action. */ gboolean imageinfo_undo( Imageinfo *imageinfo ) { Undobuffer *undo; /* Find the undo action we are to perform. */ if( !imageinfo->undo ) return( TRUE ); undo = (Undobuffer *) imageinfo->undo->data; /* We are going to undo the first action on the undo list. We must * save the area under the first undo action to the redo list. Do * the save, even if undo is disabled. */ if( !imageinfo_undo_add( imageinfo, &undo->bbox ) ) return( FALSE ); /* Add new undo area. */ imageinfo->redo = g_slist_prepend( imageinfo->redo, imageinfo->cundo ); imageinfo->cundo = NULL; /* Paint undo back. */ imageinfo_undobuffer_paste( undo ); /* Junk the undo action we have performed. */ imageinfo->undo = g_slist_remove( imageinfo->undo, undo ); imageinfo_undobuffer_free( undo ); /* Trim undo buffer. */ imageinfo_undo_trim( imageinfo ); /* Update menus. */ imageinfo_undo_changed( imageinfo ); return( TRUE ); } /* Redo a paint action, if possible. */ gboolean imageinfo_redo( Imageinfo *imageinfo ) { Undobuffer *undo; /* Find the redo action we are to perform. */ if( !imageinfo->redo ) return( TRUE ); undo = (Undobuffer *) imageinfo->redo->data; /* We are going to redo the first action on the redo list. We must * save the area under the first redo action to the undo list. Save * even if undo is disabled. */ if( !imageinfo_undo_add( imageinfo, &undo->bbox ) ) return( FALSE ); /* Add this new buffer to the undo list. */ imageinfo->undo = g_slist_prepend( imageinfo->undo, imageinfo->cundo ); imageinfo->cundo = NULL; /* Paint redo back. */ imageinfo_undobuffer_paste( undo ); /* We can junk the head of the undo list now. */ imageinfo->redo = g_slist_remove( imageinfo->redo, undo ); imageinfo_undobuffer_free( undo ); /* Trim undo buffer. */ imageinfo_undo_trim( imageinfo ); /* Update menus. */ imageinfo_undo_changed( imageinfo ); return( TRUE ); } void imageinfo_undo_clear( Imageinfo *imageinfo ) { imageinfo_undo_free( imageinfo ); imageinfo_undo_changed( imageinfo ); } static int imageinfo_draw_point_cb( IMAGE *im, int x, int y, void *a, void *b, void *c ) { IMAGE *mask = (IMAGE *) a; PEL *ink = (PEL *) b; return( im_draw_mask( im, mask, x - mask->Xsize / 2, y - mask->Ysize / 2, ink ) ); } /* Draw a line. */ gboolean imageinfo_paint_line( Imageinfo *imageinfo, Imageinfo *ink, Imageinfo *mask, int x1, int y1, int x2, int y2 ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); IMAGE *ink_im = imageinfo_get( FALSE, ink ); IMAGE *mask_im = imageinfo_get( FALSE, mask ); PEL *data = (PEL *) ink_im->data; Rect dirty, p1, p2, image, clipped; p1.width = mask_im->Xsize; p1.height = mask_im->Ysize; p1.left = x1 - mask_im->Xsize / 2; p1.top = y1 - mask_im->Ysize / 2; p2.width = mask_im->Xsize; p2.height = mask_im->Ysize; p2.left = x2 - mask_im->Xsize / 2; p2.top = y2 - mask_im->Ysize / 2; im_rect_unionrect( &p1, &p2, &dirty ); image.left = 0; image.top = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_intersectrect( &dirty, &image, &clipped ); if( im_rect_isempty( &clipped ) ) return( TRUE ); if( !imageinfo_undo_add( imageinfo, &clipped ) ) return( FALSE ); if( im_draw_line_user( im, x1, y1, x2, y2, (VipsPlotFn) imageinfo_draw_point_cb, mask_im, data, NULL ) ) { error_vips_all(); return( FALSE ); } imageinfo_area_painted( imageinfo, &dirty ); return( TRUE ); } /* Smudge a line. */ gboolean imageinfo_paint_smudge( Imageinfo *imageinfo, Rect *oper, int x1, int y1, int x2, int y2 ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); Rect p1, p2, dirty; /* Calculate bounding box for smudge. */ p1 = *oper; p1.left += x1; p1.top += y1; p2 = *oper; p2.left += x2; p2.top += y2; im_rect_unionrect( &p1, &p2, &dirty ); if( !imageinfo_undo_add( imageinfo, &dirty ) ) return( FALSE ); /* Smudge line connecting old and new points. */ if( im_draw_line_user( im, x1, y1, x2, y2, (VipsPlotFn) im_smudge, oper, NULL, NULL ) ) { error_vips_all(); return( FALSE ); } imageinfo_area_painted( imageinfo, &dirty ); return( TRUE ); } /* Flood an area. */ gboolean imageinfo_paint_flood( Imageinfo *imageinfo, Imageinfo *ink, int x, int y, gboolean blob ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); IMAGE *ink_im = imageinfo_get( FALSE, ink ); PEL *data = (PEL *) ink_im->data; Rect dirty; int result; /* Save undo area. We have to save the entire image since we don't know * how much the flood will change :( */ dirty.left = 0; dirty.top = 0; dirty.width = im->Xsize; dirty.height = im->Ysize; if( !imageinfo_undo_add( imageinfo, &dirty ) ) return( FALSE ); /* Flood! */ if( blob ) result = im_flood_blob( im, x, y, data, &dirty ); else result = im_flood( im, x, y, data, &dirty ); if( result ) { error_vips_all(); return( FALSE ); } imageinfo_area_painted( imageinfo, &dirty ); return( TRUE ); } gboolean imageinfo_paint_dropper( Imageinfo *imageinfo, Imageinfo *ink, int x, int y ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); IMAGE *ink_im = imageinfo_get( FALSE, ink ); PEL *data = (PEL *) ink_im->data; Rect dirty; if( im_readpoint( im, x, y, data ) ) { error_vips_all(); return( FALSE ); } im_invalidate( ink_im ); dirty.left = 0; dirty.top = 0; dirty.width = ink_im->Xsize; dirty.height = ink_im->Ysize; imageinfo_area_painted( ink, &dirty ); return( TRUE ); } /* Fill a rect. */ gboolean imageinfo_paint_rect( Imageinfo *imageinfo, Imageinfo *ink, Rect *area ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); IMAGE *ink_im = imageinfo_get( FALSE, ink ); PEL *data = (PEL *) ink_im->data; if( !imageinfo_undo_add( imageinfo, area ) ) return( FALSE ); if( im_draw_rect( im, area->left, area->top, area->width, area->height, 1, data ) ) { error_vips_all(); return( FALSE ); } imageinfo_area_painted( imageinfo, area ); return( TRUE ); } /* Paint text into imageinfo, return width/height in tarea. */ gboolean imageinfo_paint_text( Imageinfo *imageinfo, const char *font_name, const char *text, Rect *tarea ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); if( im_text( im, text, font_name, 0, 0, get_dpi() ) ) { error_top( _( "Unable to paint text." ) ); error_sub( _( "Unable to paint text \"%s\" in font \"%s\"." ), text, font_name ); error_vips(); return( FALSE ); } tarea->left = 0; tarea->top = 0; tarea->width = im->Xsize; tarea->height = im->Ysize; return( TRUE ); } /* Draw a nib mask. Radius 0 means a single-pixel mask. */ gboolean imageinfo_paint_nib( Imageinfo *imageinfo, int radius ) { static PEL ink[1] = { 255 }; IMAGE *im = imageinfo_get( FALSE, imageinfo ); if( radius ) { int r2 = radius * 2; IMAGE *t; if( !(t = im_open( "imageinfo_paint_nib", "p" )) ) { error_vips(); return( FALSE ); } if( im_black( t, 2 * (r2 + 1), 2 * (r2 + 1), 1 ) || im_draw_circle( t, r2, r2, r2, 1, ink ) || im_shrink( t, im, 2, 2 ) ) { im_close( t ); error_vips(); return( FALSE ); } im_close( t ); } else { if( im_black( im, 1, 1, 1 ) || im_draw_circle( im, 0, 0, 0, 1, ink ) ) return( FALSE ); } return( TRUE ); } /* Paint a mask. */ gboolean imageinfo_paint_mask( Imageinfo *imageinfo, Imageinfo *ink, Imageinfo *mask, int x, int y ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); IMAGE *ink_im = imageinfo_get( FALSE, ink ); IMAGE *mask_im = imageinfo_get( FALSE, mask ); Rect dirty, image, clipped; dirty.left = x; dirty.top = y; dirty.width = mask_im->Xsize; dirty.height = mask_im->Ysize; image.left = 0; image.top = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_intersectrect( &dirty, &image, &clipped ); if( im_rect_isempty( &clipped ) ) return( TRUE ); if( !imageinfo_undo_add( imageinfo, &clipped ) ) return( FALSE ); if( im_plotmask( im, 0, 0, (PEL *) ink_im->data, (PEL *) mask_im->data, &dirty ) ) { error_vips_all(); return( FALSE ); } imageinfo_area_painted( imageinfo, &dirty ); return( TRUE ); } /* Print a pixel. Output has to be parseable by imageinfo_from_text(). */ void imageinfo_to_text( Imageinfo *imageinfo, VipsBuf *buf ) { IMAGE *im = imageinfo_get( FALSE, imageinfo ); PEL *p = (PEL *) im->data; int i; #define PRINT_INT( T, I ) vips_buf_appendf( buf, "%d", ((T *)p)[I] ); #define PRINT_FLOAT( T, I ) vips_buf_appendg( buf, ((T *)p)[I] ); for( i = 0; i < im->Bands; i++ ) { if( i ) vips_buf_appends( buf, ", " ); switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: PRINT_INT( unsigned char, i ); break; case IM_BANDFMT_CHAR: PRINT_INT( char, i ); break; case IM_BANDFMT_USHORT: PRINT_INT( unsigned short, i ); break; case IM_BANDFMT_SHORT: PRINT_INT( short, i ); break; case IM_BANDFMT_UINT: PRINT_INT( unsigned int, i ); break; case IM_BANDFMT_INT: PRINT_INT( int, i ); break; case IM_BANDFMT_FLOAT: PRINT_FLOAT( float, i ); break; case IM_BANDFMT_COMPLEX: vips_buf_appends( buf, "(" ); PRINT_FLOAT( float, (i << 1) ); vips_buf_appends( buf, ", " ); PRINT_FLOAT( float, (i << 1) + 1 ); vips_buf_appends( buf, ")" ); break; case IM_BANDFMT_DOUBLE: PRINT_FLOAT( double, i ); break; case IM_BANDFMT_DPCOMPLEX: vips_buf_appends( buf, "(" ); PRINT_FLOAT( double, i << 1 ); vips_buf_appends( buf, ", " ); PRINT_FLOAT( double, (i << 1) + 1 ); vips_buf_appends( buf, ")" ); break; default: vips_buf_appends( buf, "???" ); break; } } } /* Set band i to value. */ static void imageinfo_from_text_band( Imageinfo *imageinfo, int i, double re, double im ) { IMAGE *image = imageinfo_get( FALSE, imageinfo ); PEL *p = (PEL *) image->data; double mod = sqrt( re*re + im*im ); if( i < 0 || i >= image->Bands ) return; #define SET_INT( T, I, X ) (((T *)p)[I] = (T) IM_RINT(X)) #define SET_FLOAT( T, I, X ) (((T *)p)[I] = (T) (X)) switch( image->BandFmt ) { case IM_BANDFMT_UCHAR: SET_INT( unsigned char, i, mod ); break; case IM_BANDFMT_CHAR: SET_INT( char, i, mod ); break; case IM_BANDFMT_USHORT: SET_INT( unsigned short, i, mod ); break; case IM_BANDFMT_SHORT: SET_INT( short, i, mod ); break; case IM_BANDFMT_UINT: SET_INT( unsigned int, i, mod ); break; case IM_BANDFMT_INT: SET_INT( int, i, mod ); break; case IM_BANDFMT_FLOAT: SET_FLOAT( float, i, mod ); break; case IM_BANDFMT_COMPLEX: SET_FLOAT( float, (i << 1), re ); SET_FLOAT( float, (i << 1) + 1, im ); break; case IM_BANDFMT_DOUBLE: SET_FLOAT( double, i, mod ); break; case IM_BANDFMT_DPCOMPLEX: SET_FLOAT( double, i << 1, re ); SET_FLOAT( double, (i << 1) + 1, im ); break; default: break; } } /* Parse a string to an imageinfo. * Strings are from imageinfo_to_text(), ie. of the form: * * 50, 0, 0 * (12,13), (14,15) * */ gboolean imageinfo_from_text( Imageinfo *imageinfo, const char *text ) { char buf[MAX_LINELENGTH]; char *p; int i; Rect dirty; #ifdef DEBUG_RGB printf( "imageinfo_from_text: in: \"\%s\"\n", text ); #endif /*DEBUG_RGB*/ im_strncpy( buf, text, MAX_LINELENGTH ); for( i = 0, p = buf; p += strspn( p, WHITESPACE ), *p; i++ ) { double re, im; if( p[0] == '(' ) { /* Complex constant. */ re = g_ascii_strtod( p + 1, NULL ); p = break_token( p, "," ); im = g_ascii_strtod( p, NULL ); p = break_token( p, ")" ); } else { /* Real constant. */ re = g_ascii_strtod( p, NULL ); im = 0; } p = break_token( p, "," ); imageinfo_from_text_band( imageinfo, i, re, im ); } #ifdef DEBUG_RGB { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "imageinfo_from_text: out: " ); imageinfo_to_text( imageinfo, &buf ); printf( "%s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_RGB*/ dirty.left = 0; dirty.top = 0; dirty.width = 1; dirty.height = 1; imageinfo_area_painted( imageinfo, &dirty ); return( TRUE ); } /* Get the image as display RGB in rgb[0-2]. */ void imageinfo_to_rgb( Imageinfo *imageinfo, double *rgb ) { Conversion *conv; Rect area; PEL *p; int i; #ifdef DEBUG_RGB { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "imageinfo_to_rgb: in: " ); imageinfo_to_text( imageinfo, &buf ); printf( "%s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_RGB*/ /* Make a temporary conv ... we hold the ref. */ conv = conversion_new( NULL ); conversion_set_synchronous( conv, TRUE ); conversion_set_image( conv, imageinfo ); g_object_ref( G_OBJECT( conv ) ); iobject_sink( IOBJECT( conv ) ); area.left = 0; area.top = 0; area.width = 1; area.height = 1; if( im_prepare( conv->ireg, &area ) ) { UNREF( conv ); return; } p = (PEL *) IM_REGION_ADDR( conv->ireg, area.left, area.top ); if( imageinfo->im->Bands < 3 ) for( i = 0; i < 3; i++ ) rgb[i] = p[0] / 255.0; else for( i = 0; i < 3; i++ ) rgb[i] = p[i] / 255.0; #ifdef DEBUG_RGB printf( "imageinfo_to_rgb: out: r = %g, g = %g, b = %g\n", rgb[0], rgb[1], rgb[2] ); #endif /*DEBUG_RGB*/ UNREF( conv ); } /* Try to overwrite an imageinfo with a display RGB colour. */ void imageinfo_from_rgb( Imageinfo *imageinfo, double *rgb ) { Imageinfogroup *imageinfogroup = IMAGEINFOGROUP( ICONTAINER( imageinfo )->parent ); IMAGE *im = imageinfo_get( FALSE, imageinfo ); Imageinfo *in, *out; IMAGE *t1, *t2; int i; Rect dirty; /* Interchange format is sRGB. FIXME ... should let other displays be used here, see ../scraps/calibrate.[hc] */ struct im_col_display *display = im_col_displays( 7 ); #ifdef DEBUG_RGB printf( "imageinfo_from_rgb: in: r = %g, g = %g, b = %g\n", rgb[0], rgb[1], rgb[2] ); #endif /*DEBUG_RGB*/ /* Make 1 pixel images for conversion. */ in = imageinfo_new_temp( imageinfogroup, reduce_context->heap, NULL, "t" ); out = imageinfo_new_temp( imageinfogroup, reduce_context->heap, NULL, "t" ); if( !in || !out ) return; if( !(t1 = im_open_local( out->im, "imageinfo_from_rgb:1", "t" )) || !(t2 = im_open_local( out->im, "imageinfo_from_rgb:1", "t" )) ) return; /* Fill in with rgb. */ im_initdesc( in->im, 1, 1, 3, IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, IM_TYPE_sRGB, 1.0, 1.0, 0, 0 ); if( im_setupout( in->im ) ) return; for( i = 0; i < 3; i++ ) ((PEL *) in->im->data)[i] = IM_RINT( rgb[i] * 255.0 ); /* To imageinfo->type. Make sure we get a float ... except for LABQ * and RAD. */ if( im->Coding == IM_CODING_LABQ ) { if( im_disp2Lab( in->im, t1, display ) || im_Lab2LabQ( t1, out->im ) ) return; } else if( im->Coding == IM_CODING_RAD ) { if( im_disp2XYZ( in->im, t1, display ) || im_float2rad( t1, out->im ) ) return; } else if( im->Coding == IM_CODING_NONE ) { switch( im->Type ) { case IM_TYPE_XYZ: if( im_disp2XYZ( in->im, out->im, display ) ) return; break; case IM_TYPE_YXY: if( im_disp2XYZ( in->im, t1, display ) || im_XYZ2Yxy( t1, out->im ) ) return; break; case IM_TYPE_LAB: if( im_disp2Lab( in->im, out->im, display ) ) return; break; case IM_TYPE_LCH: if( im_disp2Lab( in->im, t1, display ) || im_Lab2LCh( t1, out->im ) ) return; break; case IM_TYPE_UCS: if( im_disp2Lab( in->im, t1, display ) || im_Lab2LCh( t1, t2 ) || im_LCh2UCS( t2, out->im ) ) return; break; case IM_TYPE_RGB16: case IM_TYPE_GREY16: if( im_lintra( 1.0 / 256.0, in->im, 0.0, out->im ) ) return; break; case IM_TYPE_RGB: case IM_TYPE_sRGB: default: if( im_clip2fmt( in->im, out->im, IM_BANDFMT_FLOAT ) ) return; break; } } #define SET( TYPE, i ) ((TYPE *) im->data)[i] = ((float *) out->im->data)[i]; /* Now ... overwrite imageinfo. */ if( im->Coding == IM_CODING_LABQ || im->Coding == IM_CODING_RAD ) { for( i = 0; i < im->Bands; i++ ) ((PEL *) im->data)[i] = ((PEL *) out->im->data)[i]; } else { for( i = 0; i < im->Bands; i++ ) switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: SET( unsigned char, i ); break; case IM_BANDFMT_CHAR: SET( signed char, i ); break; case IM_BANDFMT_USHORT: SET( unsigned short, i ); break; case IM_BANDFMT_SHORT: SET( signed short, i ); break; case IM_BANDFMT_UINT: SET( unsigned int, i ); break; case IM_BANDFMT_INT: SET( signed int, i ); break; case IM_BANDFMT_FLOAT: SET( float, i ); break; case IM_BANDFMT_DOUBLE: SET( double, i ); break; case IM_BANDFMT_COMPLEX: SET( float, i * 2 ); SET( float, i * 2 + 1 ); break; case IM_BANDFMT_DPCOMPLEX: SET( double, i * 2 ); SET( double, i * 2 + 1 ); break; default: g_assert( FALSE ); } } im_invalidate( im ); #ifdef DEBUG_RGB { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "imageinfo_from_rgb: out: " ); imageinfo_to_text( imageinfo, &buf ); printf( "%s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_RGB*/ dirty.left = 0; dirty.top = 0; dirty.width = 1; dirty.height = 1; imageinfo_area_painted( imageinfo, &dirty ); } /* Widgets for colour edit. */ typedef struct _ColourEdit { iDialog *idlg; Imageinfo *imageinfo; GtkWidget *colour_widget; } ColourEdit; /* Done button hit. */ static void imageinfo_colour_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { ColourEdit *eds = (ColourEdit *) client; Imageinfo *imageinfo = eds->imageinfo; double rgb[4]; gtk_color_selection_get_color( GTK_COLOR_SELECTION( eds->colour_widget ), rgb ); /* This will emit "area_painted" on our imageinfo. */ imageinfo_from_rgb( imageinfo, rgb ); nfn( sys, IWINDOW_YES ); } /* Build the insides of colour edit. */ static void imageinfo_colour_buildedit( iDialog *idlg, GtkWidget *work, ColourEdit *eds ) { Imageinfo *imageinfo = eds->imageinfo; double rgb[4]; eds->colour_widget = gtk_color_selection_new(); gtk_color_selection_set_has_opacity_control( GTK_COLOR_SELECTION( eds->colour_widget ), FALSE ); imageinfo_to_rgb( imageinfo, rgb ); gtk_color_selection_set_color( GTK_COLOR_SELECTION( eds->colour_widget ), rgb ); gtk_box_pack_start( GTK_BOX( work ), eds->colour_widget, TRUE, TRUE, 2 ); gtk_widget_show_all( work ); } void imageinfo_colour_edit( GtkWidget *parent, Imageinfo *imageinfo ) { ColourEdit *eds = INEW( NULL, ColourEdit ); GtkWidget *idlg; eds->imageinfo = imageinfo; idlg = idialog_new(); iwindow_set_title( IWINDOW( idlg ), "Edit Colour" ); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) imageinfo_colour_buildedit, eds, NULL, NULL ); idialog_set_callbacks( IDIALOG( idlg ), iwindow_true_cb, NULL, idialog_free_client, eds ); idialog_add_ok( IDIALOG( idlg ), imageinfo_colour_done_cb, "Set Colour" ); iwindow_set_parent( IWINDOW( idlg ), parent ); idialog_set_iobject( IDIALOG( idlg ), IOBJECT( imageinfo ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } ================================================ FILE: src/imageinfo.h ================================================ /* Decls for imageinfo.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Meta we attach for the filename we loaded from. */ #define ORIGINAL_FILENAME "original-filename" /* Group imageinfo with this. */ #define TYPE_IMAGEINFOGROUP (imageinfogroup_get_type()) #define IMAGEINFOGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ TYPE_IMAGEINFOGROUP, Imageinfogroup )) #define IMAGEINFOGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_IMAGEINFOGROUP, ImageinfogroupClass)) #define IS_IMAGEINFOGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IMAGEINFOGROUP )) #define IS_IMAGEINFOGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IMAGEINFOGROUP )) #define IMAGEINFOGROUP_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_IMAGEINFOGROUP, ImageinfogroupClass )) typedef struct _Imageinfogroup { iContainer parent_object; /* Hash from filename to list of imageinfo. We can't use the * icontainer hash, since our filenames are not unique (we can have * the same file loaded several times, if some other application is * changing our files behind our back). */ GHashTable *filename_hash; } Imageinfogroup; typedef struct _ImageinfogroupClass { iContainerClass parent_class; } ImageinfogroupClass; GType imageinfogroup_get_type( void ); Imageinfogroup *imageinfogroup_new( void ); /* An image. */ #define TYPE_IMAGEINFO (imageinfo_get_type()) #define IMAGEINFO( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IMAGEINFO, Imageinfo )) #define IMAGEINFO_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_IMAGEINFO, ImageinfoClass)) #define IS_IMAGEINFO( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IMAGEINFO )) #define IS_IMAGEINFO_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IMAGEINFO )) #define IMAGEINFO_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_IMAGEINFO, ImageinfoClass )) /* A fragment of an undo buffer. */ typedef struct _Undofragment { struct _Undobuffer *undo; /* Main undo area */ IMAGE *im; /* Old area */ Rect pos; /* Where we took it from */ } Undofragment; /* Hold a list of the above, a bounding box for this list and a link back to * the main imageinfo. */ typedef struct _Undobuffer { struct _Imageinfo *imageinfo; /* Main paint area */ GSList *frags; /* List of paint fragments */ Rect bbox; /* Bounding box for frags */ } Undobuffer; /* Attach one of these to any IMAGE we monitor. It has the same lifetime as * the IMAGE and gets zapped by the imageinfo on dispose. This lets us spot * IMAGE events after the holding Imageinfo has gone. */ typedef struct _Imageinfoproxy { IMAGE *im; Imageinfo *imageinfo; } Imageinfoproxy; /* A VIPS image wrapped up nicely. */ struct _Imageinfo { Managed parent_object; IMAGE *im; /* Image we manage, LUT if delayed */ IMAGE *mapped_im; /* Cache image mapped-thru-lut here */ IMAGE *identity_lut; /* For base images, keep an id lut if poss */ Imageinfo *underlying; /* If we're a LUT, the image we are a LUT of */ Imageinfoproxy *proxy; /* Proxy for IMAGE callbacks */ gboolean dfile; /* delete_file on final close */ char *delete_filename; /* and the file we delete */ gboolean from_file; /* Set if ->name is a user file */ time_t mtime; /* What mtime was when we loaded this file */ /* Exprs which are thought to have this image as their value. See * expr_value_new(). */ GSList *exprs; /* Set if we've checked with the user that it's OK to paint on this * imageinfo. */ gboolean ok_to_paint; /* Undo/redo buffers. */ GSList *undo; /* List of undo buffers */ GSList *redo; /* List of redo buffers */ Undobuffer *cundo; /* Current buffer */ /* Have we attached progress stuff to this ii? */ gboolean monitored; /* If we're from a file, the timestamp on the file we loaded from ... * used to spot changes. */ time_t check_mtime; guint check_tid; }; typedef struct _ImageinfoClass { ManagedClass parent_class; /* An area of the screen needs repainting. This can happen for regions * being dragged, for example, and doesn't always mean pixels have * changed. */ void (*area_changed)( Imageinfo *, Rect * ); /* An area of the image has been paintboxed ... invalidate caches and * trigger area_changed. */ void (*area_painted)( Imageinfo *, Rect * ); /* Our IMAGE* has signaled "invalidate". This can happen indirectly: * if we paint on an image, im_invalidate() will trigger on that image * and all derived images. */ void (*invalidate)( Imageinfo * ); /* Update undo/redo button sensitivities. */ void (*undo_changed)( Imageinfo * ); /* The underlying file has changed ... higher levels should try to * reload. */ void (*file_changed)( Imageinfo * ); } ImageinfoClass; void *imageinfo_area_changed( Imageinfo *imageinfo, Rect *dirty ); void *imageinfo_area_painted( Imageinfo *imageinfo, Rect *dirty ); void *imageinfo_expr_remove( Expr *expr, Imageinfo *imageinfo ); void imageinfo_expr_add( Imageinfo *imageinfo, Expr *expr ); GSList *imageinfo_expr_which( Imageinfo *imageinfo ); IMAGE *imageinfo_get_underlying( Imageinfo *imageinfo ); GType imageinfo_get_type( void ); Imageinfo *imageinfo_new( Imageinfogroup *imageinfogroup, Heap *heap, IMAGE *im, const char *name ); Imageinfo *imageinfo_new_temp( Imageinfogroup *imageinfogroup, Heap *heap, const char *name, const char *mode ); Imageinfo *imageinfo_new_from_pixbuf( Imageinfogroup *imageinfogroup, Heap *heap, GdkPixbuf *pixbuf ); void imageinfo_set_underlying( Imageinfo *top_imageinfo, Imageinfo *imageinfo ); gboolean imageinfo_is_from_file( Imageinfo *imageinfo ); Imageinfo *imageinfo_new_input( Imageinfogroup *imageinfogroup, GtkWidget *parent, Heap *heap, const char *name ); IMAGE *imageinfo_get( gboolean use_lut, Imageinfo *imageinfo ); gboolean imageinfo_same_underlying( Imageinfo *imageinfo[], int n ); gboolean imageinfo_write( Imageinfo *imageinfo, const char *filename ); gboolean imageinfo_check_paintable( Imageinfo *imageinfo, GtkWidget *parent, iWindowNotifyFn nfn, void *sys ); void imageinfo_note( Symbol *sym, Imageinfo *imageinfo ); void imageinfo_forget( Symbol *sym, Imageinfo *imageinfo ); GSList *imageinfo_which( Imageinfo *im ); void imageinfo_make_sub( Imageinfo *out, int n, Imageinfo **in ); void imageinfo_mark( Imageinfo *imageinfo ); Imageinfo *imageinfo_sym_image( Symbol *sym ); void imageinfo_undo_mark( Imageinfo *imageinfo ); gboolean imageinfo_undo( Imageinfo *imageinfo ); gboolean imageinfo_redo( Imageinfo *imageinfo ); void imageinfo_undo_clear( Imageinfo *imageinfo ); gboolean imageinfo_paint_line( Imageinfo *imageinfo, Imageinfo *ink, Imageinfo *mask, int x1, int y1, int x2, int y2 ); gboolean imageinfo_paint_flood( Imageinfo *imageinfo, Imageinfo *ink, int x, int y, gboolean blob ); gboolean imageinfo_paint_smudge( Imageinfo *imageinfo, Rect *oper, int x1, int y1, int x2, int y2 ); gboolean imageinfo_paint_dropper( Imageinfo *imageinfo, Imageinfo *ink, int x, int iy ); gboolean imageinfo_paint_rect( Imageinfo *imageinfo, Imageinfo *ink, Rect *area ); gboolean imageinfo_paint_text( Imageinfo *imageinfo, const char *font_name, const char *text, Rect *tarea ); gboolean imageinfo_paint_nib( Imageinfo *imageinfo, int nib_radius ); gboolean imageinfo_paint_mask( Imageinfo *imageinfo, Imageinfo *ink, Imageinfo *mask, int x, int y ); void imageinfo_to_text( Imageinfo *imageinfo, VipsBuf *buf ); gboolean imageinfo_from_text( Imageinfo *imageinfo, const char *text ); void imageinfo_to_rgb( Imageinfo *imageinfo, double *rgb ); void imageinfo_from_rgb( Imageinfo *imageinfo, double *rgb ); void imageinfo_colour_edit( GtkWidget *parent, Imageinfo *imageinfo ); ================================================ FILE: src/imagemodel.c ================================================ /* All the model stuff for an imageview window. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Our signals. */ enum { SIG_IMAGEINFO_CHANGED, /* Imageinfo we hold has been replaced */ SIG_LAST }; static iObjectClass *parent_class = NULL; static guint imagemodel_signals[SIG_LAST] = { 0 }; void * imagemodel_imageinfo_changed( Imagemodel *imagemodel ) { #ifdef DEBUG printf( "imagemodel_imageinfo_changed: " ); iobject_print( IOBJECT( imagemodel ) ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( imagemodel ), imagemodel_signals[SIG_IMAGEINFO_CHANGED], 0 ); return( NULL ); } /* Is a state a paint state, ie. one which might alter the image? We warn * before going to one of these. */ gboolean imagemodel_state_paint( ImagemodelState state ) { static gboolean state_paint[IMAGEMODEL_LAST] = { FALSE, /* IMAGEMODEL_SELECT */ FALSE, /* IMAGEMODEL_PAN */ FALSE, /* IMAGEMODEL_MAGIN */ FALSE, /* IMAGEMODEL_MAGOUT */ FALSE, /* IMAGEMODEL_DROPPER */ TRUE, /* IMAGEMODEL_PEN */ TRUE, /* IMAGEMODEL_LINE */ TRUE, /* IMAGEMODEL_RECT */ TRUE, /* IMAGEMODEL_FLOOD */ TRUE, /* IMAGEMODEL_BLOB */ TRUE, /* IMAGEMODEL_TEXT */ TRUE /* IMAGEMODEL_SMUDGE */ }; g_assert( state < IMAGEMODEL_LAST ); return( state_paint[state] ); } #ifdef DEBUG static const char * imagemodel_state( ImagemodelState state ) { switch( state ) { case IMAGEMODEL_SELECT: return( "IMAGEMODEL_SELECT" ); case IMAGEMODEL_PAN: return( "IMAGEMODEL_PAN" ); case IMAGEMODEL_MAGIN: return( "IMAGEMODEL_MAGIN" ); case IMAGEMODEL_MAGOUT: return( "IMAGEMODEL_MAGOUT" ); case IMAGEMODEL_DROPPER: return( "IMAGEMODEL_DROPPER" ); case IMAGEMODEL_PEN: return( "IMAGEMODEL_PEN" ); case IMAGEMODEL_LINE: return( "IMAGEMODEL_LINE" ); case IMAGEMODEL_RECT: return( "IMAGEMODEL_RECT" ); case IMAGEMODEL_FLOOD: return( "IMAGEMODEL_FLOOD" ); case IMAGEMODEL_BLOB: return( "IMAGEMODEL_BLOB" ); case IMAGEMODEL_TEXT: return( "IMAGEMODEL_TEXT" ); case IMAGEMODEL_SMUDGE: return( "IMAGEMODEL_SMUDGE" ); default: g_assert( FALSE ); } } #endif /*DEBUG*/ static void imagemodel_dispose( GObject *gobject ) { Imagemodel *imagemodel; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_IMAGEMODEL( gobject ) ); imagemodel = IMAGEMODEL( gobject ); #ifdef DEBUG printf( "imagemodel_dispose %p: ", imagemodel ); iobject_print( IOBJECT( imagemodel ) ); #endif /*DEBUG*/ FREESID( imagemodel->iimage_changed_sid, imagemodel->iimage ); FREESID( imagemodel->iimage_destroy_sid, imagemodel->iimage ); FREESID( imagemodel->conv_changed_sid, imagemodel->conv ); FREESID( imagemodel->conv_imageinfo_changed_sid, imagemodel->conv ); UNREF( imagemodel->conv ); MANAGED_UNREF( imagemodel->ink ); IM_FREE( imagemodel->font_name ); IM_FREE( imagemodel->text ); MANAGED_UNREF( imagemodel->text_mask ); MANAGED_UNREF( imagemodel->nib ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void imagemodel_changed( iObject *iobject ) { Imagemodel *imagemodel = IMAGEMODEL( iobject ); #ifdef DEBUG printf( "imagemodel_changed: state = %s ", imagemodel_state( imagemodel->state ) ); iobject_print( IOBJECT( imagemodel ) ); #endif /*DEBUG*/ conversion_set_params( imagemodel->conv, imagemodel->show_convert, imagemodel->scale, imagemodel->offset, imagemodel->falsecolour, imagemodel->type ); /* Update prefs. */ prefs_set( "DISPLAY_RULERS", "%s", bool_to_char( imagemodel->show_rulers ) ); prefs_set( "DISPLAY_STATUS", "%s", bool_to_char( imagemodel->show_status ) ); prefs_set( "DISPLAY_CONVERSION", "%s", bool_to_char( imagemodel->show_convert ) ); /* If the paint bar is on, we want to be in synchronous paint * mode. Even if we're not painting, we need this for * undo/redo to work. */ conversion_set_synchronous( imagemodel->conv, imagemodel->show_paintbox ); IOBJECT_CLASS( parent_class )->changed( iobject ); } static void imagemodel_class_init( ImagemodelClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = imagemodel_dispose; iobject_class->changed = imagemodel_changed; imagemodel_signals[SIG_IMAGEINFO_CHANGED] = g_signal_new( "imageinfo_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ImagemodelClass, imageinfo_changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } /* Remake the ink image to match ii. */ static void imagemodel_refresh_ink( Imagemodel *imagemodel, Imageinfo *ii ) { IMAGE *main_im = imageinfo_get( FALSE, ii ); IMAGE *ink_im = imageinfo_get( FALSE, imagemodel->ink ); if( ink_im && ink_im->Bands == main_im->Bands && ink_im->BandFmt == main_im->BandFmt && ink_im->Coding == main_im->Coding && ink_im->Type == main_im->Type ) return; MANAGED_UNREF( imagemodel->ink ); if( (imagemodel->ink = imageinfo_new_temp( main_imageinfogroup, reduce_context->heap, NULL, "t" )) ) { MANAGED_REF( imagemodel->ink ); im_initdesc( imagemodel->ink->im, 1, 1, main_im->Bands, main_im->Bbits, main_im->BandFmt, main_im->Coding, main_im->Type, 1.0, 1.0, 0, 0 ); if( im_setupout( imagemodel->ink->im ) ) MANAGED_UNREF( imagemodel->ink ); } if( imagemodel->ink && imagemodel->ink->im && imagemodel->ink->im->data ) memset( imagemodel->ink->im->data, 0, IM_IMAGE_SIZEOF_LINE( imagemodel->ink->im ) ); } static void imagemodel_conv_changed_cb( Conversion *conv, Imagemodel *imagemodel ) { if( conv->ii ) imagemodel_refresh_ink( imagemodel, conv->ii ); iobject_changed( IOBJECT( imagemodel ) ); } static void imagemodel_conv_imageinfo_changed_cb( Conversion *conv, Imagemodel *imagemodel ) { imagemodel_imageinfo_changed( imagemodel ); } static void imagemodel_init( Imagemodel *imagemodel ) { imagemodel->iimage = NULL; imagemodel->iimage_changed_sid = 0; imagemodel->iimage_destroy_sid = 0; imagemodel->conv = conversion_new( NULL ); g_object_ref( G_OBJECT( imagemodel->conv ) ); iobject_sink( IOBJECT( imagemodel->conv ) ); imagemodel->conv_changed_sid = g_signal_connect( G_OBJECT( imagemodel->conv ), "changed", G_CALLBACK( imagemodel_conv_changed_cb ), imagemodel ); imagemodel->conv_imageinfo_changed_sid = g_signal_connect( G_OBJECT( imagemodel->conv ), "imageinfo_changed", G_CALLBACK( imagemodel_conv_imageinfo_changed_cb ), imagemodel ); imagemodel->conv->priority = 1; imagemodel->show_rulers = DISPLAY_RULERS; imagemodel->rulers_mm = FALSE; imagemodel->rulers_offset = FALSE; imagemodel->show_status = DISPLAY_STATUS; imagemodel->show_paintbox = FALSE; imagemodel->nib_radius = 0; imagemodel->nib = NULL; imagemodel->ink = NULL; imagemodel->font_name = im_strdup( NULL, PAINTBOX_FONT ); imagemodel->text = NULL; imagemodel->text_mask = NULL; imagemodel->show_convert = DISPLAY_CONVERSION; imagemodel->scale = 1.0; imagemodel->offset = 0.0; imagemodel->falsecolour = FALSE; imagemodel->type = TRUE; } GType imagemodel_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ImagemodelClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) imagemodel_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Imagemodel ), 32, /* n_preallocs */ (GInstanceInitFunc) imagemodel_init, }; type = g_type_register_static( TYPE_IOBJECT, "Imagemodel", &info, 0 ); } return( type ); } static void imagemodel_iimage_destroy_cb( iImage *iimage, Imagemodel *imagemodel ) { imagemodel->iimage = NULL; imagemodel->iimage_destroy_sid = 0; imagemodel->iimage_changed_sid = 0; } static void imagemodel_iimage_changed_cb( iImage *iimage, Imagemodel *imagemodel ) { conversion_set_image( imagemodel->conv, iimage->value.ii ); } static void imagemodel_link( Imagemodel *imagemodel, iImage *iimage ) { Row *row = HEAPMODEL( iimage )->row; imagemodel->iimage = iimage; imagemodel->iimage_destroy_sid = g_signal_connect( G_OBJECT( iimage ), "destroy", G_CALLBACK( imagemodel_iimage_destroy_cb ), imagemodel ); imagemodel->iimage_changed_sid = g_signal_connect( G_OBJECT( iimage ), "changed", G_CALLBACK( imagemodel_iimage_changed_cb ), imagemodel ); imagemodel->scale = row->ws->scale; imagemodel->offset = row->ws->offset; /* Install image. */ conversion_set_image( imagemodel->conv, iimage->value.ii ); /* Set name ... handy for debugging. */ iobject_set( IOBJECT( imagemodel ), row_name( HEAPMODEL( iimage )->row ), NULL ); } Imagemodel * imagemodel_new( iImage *iimage ) { Imagemodel *imagemodel; imagemodel = g_object_new( TYPE_IMAGEMODEL, NULL ); if( iimage ) imagemodel_link( imagemodel, iimage ); #ifdef DEBUG printf( "imagemodel_new: " ); iobject_print( IOBJECT( imagemodel ) ); #endif /*DEBUG*/ return( imagemodel ); } /* Callback for check_paintable() in imagemodel_set_state. */ static void imagemodel_set_paintbox_cb( void *client, iWindowResult result ) { Imagemodel *imagemodel = IMAGEMODEL( client ); #ifdef DEBUG printf( "imagemodel_set_paintbox_cb: pend_state = %s\n", imagemodel_state( imagemodel->pend_state ) ); #endif /*DEBUG*/ if( result == IWINDOW_YES ) { imagemodel_set_paintbox( imagemodel, TRUE ); if( imagemodel->state != imagemodel->pend_state ) { imagemodel->state = imagemodel->pend_state; iobject_changed( IOBJECT( imagemodel ) ); } } } /* Set the viewer state. We can't always do this immediately, we may need to * ask the user if the change is OK. Return TRUE if we were able to make the * change now. */ gboolean imagemodel_set_state( Imagemodel *imagemodel, ImagemodelState state, GtkWidget *parent ) { gboolean changed = FALSE; #ifdef DEBUG printf( "imagemodel_set_state: %s\n", imagemodel_state( state ) ); #endif /*DEBUG*/ if( state != imagemodel->state && imagemodel_state_paint( state ) ) { /* Check and warn on this image first. */ imagemodel->pend_state = state; imageinfo_check_paintable( imagemodel->conv->ii, parent, imagemodel_set_paintbox_cb, imagemodel ); /* We may not have set the state yet ... signal "changed" * to flick whatever asked for this change (eg. View * menu) back to the old state. */ changed = TRUE; } else if( state != imagemodel->state ) { imagemodel->state = state; changed = TRUE; } if( changed ) iobject_changed( IOBJECT( imagemodel ) ); return( imagemodel->state == state ); } void imagemodel_set_rulers( Imagemodel *imagemodel, gboolean show_rulers ) { if( imagemodel->show_rulers != show_rulers ) { imagemodel->show_rulers = show_rulers; iobject_changed( IOBJECT( imagemodel ) ); } } void imagemodel_set_paintbox( Imagemodel *imagemodel, gboolean show_paintbox ) { if( imagemodel->show_paintbox != show_paintbox ) { #ifdef DEBUG printf( "imagemodel_set_paintbox: " ); iobject_print( IOBJECT( imagemodel ) ); #endif /*DEBUG*/ imagemodel->show_paintbox = show_paintbox; /* If the paint bar is off, we want to not be in a paint mode. */ if( !imagemodel->show_paintbox && imagemodel_state_paint( imagemodel->state ) ) imagemodel_set_state( imagemodel, IMAGEMODEL_SELECT, NULL ); iobject_changed( IOBJECT( imagemodel ) ); } } void imagemodel_set_status( Imagemodel *imagemodel, gboolean show_status ) { if( imagemodel->show_status != show_status ) { imagemodel->show_status = show_status; iobject_changed( IOBJECT( imagemodel ) ); } } void imagemodel_set_convert( Imagemodel *imagemodel, gboolean show_convert ) { if( imagemodel->show_convert != show_convert ) { imagemodel->show_convert = show_convert; iobject_changed( IOBJECT( imagemodel ) ); } } /* Update the text_mask. imagemodel->text is kept up to date with what's in the * paintbox text widget, call this just before a paint action to render the * mask. */ gboolean imagemodel_refresh_text( Imagemodel *imagemodel ) { const char *text = imagemodel->text; if( !text || strspn( text, WHITESPACE ) == strlen( text ) ) { error_top( _( "No text specified." ) ); error_sub( _( "Enter some text to paint in the entry widget at " "the top of the window." ) ); return( FALSE ); } MANAGED_UNREF( imagemodel->text_mask ); if( !(imagemodel->text_mask = imageinfo_new_temp( main_imageinfogroup, reduce_context->heap, NULL, "t" )) ) return( FALSE ); MANAGED_REF( imagemodel->text_mask ); if( !imageinfo_paint_text( imagemodel->text_mask, imagemodel->font_name, imagemodel->text, &imagemodel->text_area ) ) return( FALSE ); return( TRUE ); } gboolean imagemodel_refresh_nib( Imagemodel *imagemodel ) { MANAGED_UNREF( imagemodel->nib ); if( !(imagemodel->nib = imageinfo_new_temp( main_imageinfogroup, reduce_context->heap, NULL, "t" )) ) return( FALSE ); MANAGED_REF( imagemodel->nib ); if( !imageinfo_paint_nib( imagemodel->nib, imagemodel->nib_radius ) ) return( FALSE ); return( TRUE ); } /* After a paint action: mark all subsequent things dirty, recalc if prefs say * so. */ void imagemodel_paint_recalc( Imagemodel *imagemodel ) { Classmodel *classmodel = CLASSMODEL( imagemodel->iimage ); Row *row = HEAPMODEL( classmodel )->row; #ifdef DEBUG printf( "imagemodel_paint_recalc: " ); iobject_print( IOBJECT( imagemodel ) ); #endif /*DEBUG*/ expr_dirty_intrans( row->expr, link_serial_new() ); if( PAINTBOX_RECOMP ) symbol_recalculate_all(); } ================================================ FILE: src/imagemodel.h ================================================ /* All the model stuff for the widgets making up a single imageview window. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IMAGEMODEL (imagemodel_get_type()) #define IMAGEMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IMAGEMODEL, Imagemodel )) #define IMAGEMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_IMAGEMODEL, ImagemodelClass)) #define IS_IMAGEMODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IMAGEMODEL )) #define IS_IMAGEMODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IMAGEMODEL )) #define IMAGEMODEL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_IMAGEMODEL, ImagemodelClass )) /* Input states. */ typedef enum _ImagemodelState { IMAGEMODEL_SELECT = 0, /* Pointer */ IMAGEMODEL_PAN, /* Hand panner */ IMAGEMODEL_MAGIN, /* Zoom in */ IMAGEMODEL_MAGOUT, /* Zoom out */ IMAGEMODEL_DROPPER, /* Ink dropper */ IMAGEMODEL_PEN, /* Pen */ IMAGEMODEL_LINE, /* Line drawing tool */ IMAGEMODEL_RECT, /* Rectangle tool */ IMAGEMODEL_FLOOD, /* Flood-fill tool */ IMAGEMODEL_BLOB, /* Blob flood-fill tool */ IMAGEMODEL_TEXT, /* Text tool */ IMAGEMODEL_SMUDGE, /* Blur */ IMAGEMODEL_LAST } ImagemodelState; struct _Imagemodel { iObject parent_class; /* Context. */ iImage *iimage; /* iImage we represent, if any */ guint iimage_changed_sid; guint iimage_destroy_sid; /* State held in sub-objects. */ Conversion *conv; /* Conversion to screen */ guint conv_changed_sid; guint conv_imageinfo_changed_sid; Rect visible; /* Visible part of canvas */ /* Input state. */ ImagemodelState state; ImagemodelState save_state; /* Old state, during temp actions */ ImagemodelState pend_state; /* To-be state, during delayed switch */ /* Rulers. */ gboolean show_rulers; gboolean rulers_mm; gboolean rulers_offset; /* Status bar. */ gboolean show_status; /* Paintbox. */ gboolean show_paintbox; /* Visible/not */ int nib_radius; /* Selected radius */ Imageinfo *nib; /* Generated nib mask */ Imageinfo *ink; /* 1x1 pixel ink image */ char *font_name; /* Selected font name */ char *text; /* Text to render */ Imageinfo *text_mask; /* As a bitmap */ Rect text_area; /* Text geometry */ /* Display control bar. */ gboolean show_convert; double scale; /* Contrast/brightness */ double offset; gboolean falsecolour; /* False colour display on */ gboolean type; /* Interpret type field */ }; typedef struct _ImagemodelClass { iObjectClass parent_class; /* Imagemodel has a new imageinfo. */ void (*imageinfo_changed)( Imagemodel * ); } ImagemodelClass; void *imagemodel_imageinfo_changed( Imagemodel *imagemodel ); gboolean imagemodel_state_paint( ImagemodelState state ); GType imagemodel_get_type( void ); Imagemodel *imagemodel_new( iImage *iimage ); gboolean imagemodel_set_state( Imagemodel *imagemodel, ImagemodelState state, GtkWidget *parent ); void imagemodel_set_rulers( Imagemodel *imagemodel, gboolean show_rulers ); void imagemodel_set_paintbox( Imagemodel *imagemodel, gboolean show_paintbox ); void imagemodel_set_status( Imagemodel *imagemodel, gboolean show_status ); void imagemodel_set_convert( Imagemodel *imagemodel, gboolean show_convert ); gboolean imagemodel_refresh_text( Imagemodel *imagemodel ); gboolean imagemodel_refresh_nib( Imagemodel *imagemodel ); void imagemodel_paint_recalc( Imagemodel *imagemodel ); ================================================ FILE: src/imagepresent.c ================================================ /* Imagepresent widget code. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* Define to trace button press events. #define EVENT */ /* Snap if closer than this. */ const int imagepresent_snap_threshold = 10; /* Cursor shape in id for each state. */ iWindowShape imagepresent_cursors[IMAGEMODEL_LAST] = { IWINDOW_SHAPE_EDIT, /* IMAGEMODEL_SELECT */ IWINDOW_SHAPE_MOVE, /* IMAGEMODEL_PAN */ IWINDOW_SHAPE_MAGIN, /* IMAGEMODEL_MAGIN */ IWINDOW_SHAPE_MAGOUT, /* IMAGEMODEL_MAGOUT */ IWINDOW_SHAPE_DROPPER, /* IMAGEMODEL_DROPPER */ IWINDOW_SHAPE_PEN, /* IMAGEMODEL_PEN */ IWINDOW_SHAPE_PEN, /* IMAGEMODEL_LINE */ IWINDOW_SHAPE_RECT, /* IMAGEMODEL_RECT */ IWINDOW_SHAPE_FLOOD, /* IMAGEMODEL_FLOOD */ IWINDOW_SHAPE_FLOOD, /* IMAGEMODEL_BLOB */ IWINDOW_SHAPE_TEXT, /* IMAGEMODEL_TEXT */ IWINDOW_SHAPE_SMUDGE /* IMAGEMODEL_SMUDGE */ }; /* Gdk keysyms, and the zooms we set for each. */ typedef struct _ImagepresentKeymap { guint keyval; int zoom; } ImagepresentKeymap; static ImagepresentKeymap imagepresent_keymap[] = { { GDK_1, 1 }, { GDK_2, 2 }, { GDK_3, 3 }, { GDK_4, 4 }, { GDK_5, 5 }, { GDK_6, 6 }, { GDK_7, 7 }, { GDK_8, 8 }, { GDK_9, 9 } }; /* Parent class. */ static GtkBinClass *parent_class = NULL; static void imagepresent_destroy( GtkObject *object ) { Imagepresent *ip = IMAGEPRESENT( object ); #ifdef DEBUG printf( "imagepresent_destroy\n" ); #endif /*DEBUG*/ IM_FREEF( g_source_remove, ip->scroll_tid ); IM_FREEF( iwindow_cursor_context_destroy, ip->cntxt ); DESTROY_GTK( ip->ruler_menu ); if( ip->imagemodel ) { iImage *iimage = ip->imagemodel->iimage; if( iimage ) iimage->views = g_slist_remove( iimage->views, ip ); UNREF( ip->imagemodel ); } GTK_OBJECT_CLASS( parent_class )->destroy( object ); /* Child views should all have removed themselves. */ g_assert( ip->regionviews == NULL ); } static void imagepresent_size_request( GtkWidget *widget, GtkRequisition *requisition ) { GtkBin *bin = GTK_BIN( widget ); gint focus_width; gint focus_pad; gtk_widget_style_get( widget, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL ); requisition->width = 2 * (focus_width + focus_pad); requisition->height = 2 * (focus_width + focus_pad); if( bin->child && GTK_WIDGET_VISIBLE( bin->child ) ) { GtkRequisition child_requisition; gtk_widget_size_request( bin->child, &child_requisition ); requisition->width += child_requisition.width; requisition->height += child_requisition.height; } } static void imagepresent_size_allocate( GtkWidget *widget, GtkAllocation *allocation ) { GtkBin *bin = GTK_BIN( widget ); widget->allocation = *allocation; if( bin->child && GTK_WIDGET_VISIBLE( bin->child ) ) { gint focus_width; gint focus_pad; GtkAllocation child_allocation; gtk_widget_style_get( widget, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL ); child_allocation.x = allocation->x + focus_width + focus_pad; child_allocation.y = allocation->y + focus_width + focus_pad; child_allocation.width = IM_MAX( 1, allocation->width - 2 * (focus_width + focus_pad) ); child_allocation.height = IM_MAX( 1, allocation->height - 2 * (focus_width + focus_pad) ); gtk_widget_size_allocate( bin->child, &child_allocation ); } } static gboolean imagepresent_expose_event( GtkWidget *widget, GdkEventExpose *event ) { if( GTK_WIDGET_DRAWABLE( widget ) ) { if( GTK_WIDGET_HAS_FOCUS( widget ) ) { gint focus_pad; int x, y, width, height; gtk_widget_style_get( widget, "focus-padding", &focus_pad, NULL ); x = widget->allocation.x + focus_pad; y = widget->allocation.y + focus_pad; width = widget->allocation.width - 2 * focus_pad; height = widget->allocation.height - 2 * focus_pad; gtk_paint_focus( widget->style, widget->window, GTK_WIDGET_STATE( widget ), &event->area, widget, "imagepresent", x, y, width, height ); } GTK_WIDGET_CLASS( parent_class )->expose_event( widget, event ); } return( FALSE ); } /* Connect to our enclosing iwnd on realize. */ static void imagepresent_realize( GtkWidget *widget ) { Imagepresent *ip = IMAGEPRESENT( widget ); iWindow *iwnd = IWINDOW( gtk_widget_get_toplevel( widget ) ); if( !ip->cntxt ) ip->cntxt = iwindow_cursor_context_new( iwnd, 0, "imagepresent" ); /* Set initial state. _realize() is too late ... the _refresh() has * already happened. */ iwindow_cursor_context_set_cursor( ip->cntxt, imagepresent_cursors[ip->imagemodel->state] ); GTK_WIDGET_CLASS( parent_class )->realize( widget ); } static void imagepresent_class_init( ImagepresentClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; /* Init parent class. */ parent_class = g_type_class_peek_parent( class ); object_class->destroy = imagepresent_destroy; widget_class->size_request = imagepresent_size_request; widget_class->size_allocate = imagepresent_size_allocate; widget_class->expose_event = imagepresent_expose_event; widget_class->realize = imagepresent_realize; /* Init default methods. */ /* Static class init. */ } /* Rethink rulers. */ static void imagepresent_hruler_rethink( Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; IMAGE *im = imageinfo_get( FALSE, conv->ii ); /* Try to get the ruler width: same as the whole of the scrolled * window. */ int ruler_width = GTK_WIDGET( ip->swin )->allocation.width; double from = imagemodel->visible.left; double to = from + ruler_width; double pos = ip->last_x; double scale; if( imagemodel->rulers_offset && im ) { from -= im->Xoffset; to -= im->Xoffset; pos -= im->Xoffset; } scale = conversion_dmag( conv->mag ); if( imagemodel->rulers_mm && im ) scale *= im->Xres; from /= scale; to /= scale; pos /= scale; gtk_ruler_set_range( GTK_RULER( ip->hrule ), from, to, pos, to - from ); } static void imagepresent_vruler_rethink( Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; IMAGE *im = imageinfo_get( FALSE, conv->ii ); /* Try to get the ruler height: same as the whole of the scrolled * window. */ int ruler_height = GTK_WIDGET( ip->swin )->allocation.height; double from = imagemodel->visible.top; double to = from + ruler_height; double pos = ip->last_y; double scale; if( imagemodel->rulers_offset && im ) { from -= im->Yoffset; to -= im->Yoffset; pos -= im->Yoffset; } scale = conversion_dmag( conv->mag ); if( imagemodel->rulers_mm && im ) scale *= im->Yres; from /= scale; to /= scale; pos /= scale; gtk_ruler_set_range( GTK_RULER( ip->vrule ), from, to, pos, to - from ); } /* Zoom with the mouse clicked at position x, y in canvas coordinates. */ static void imagepresent_zoom_in( Imagepresent *ip, int x, int y ) { Conversion *conv = ip->imagemodel->conv; int ix, iy; conversion_disp_to_im( conv, x, y, &ix, &iy ); imagepresent_set_mag_pos( ip, conversion_double( conv->mag ), ix, iy ); } static void imagepresent_zoom_in_centre( Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; imagepresent_zoom_in( ip, IM_RECT_HCENTRE( &imagemodel->visible ), IM_RECT_VCENTRE( &imagemodel->visible ) ); } static void imagepresent_zoom_out( Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; int ix, iy; /* Current centre of window, image cods. */ conversion_disp_to_im( conv, IM_RECT_HCENTRE( &imagemodel->visible ), IM_RECT_VCENTRE( &imagemodel->visible ), &ix, &iy ); imagepresent_set_mag_pos( ip, conversion_halve( conv->mag ), ix, iy ); } /* Scroll events ... handle mousewheel shortcuts here. */ static gboolean imagepresent_scroll_event_cb( GtkWidget *widget, GdkEventScroll *ev, Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Rect *visible = &imagemodel->visible; gboolean handled; /* Gimp uses page_incr / 4 I think, but then scroll speed varies with * window size, which is pretty odd. Just use a constant. */ const int incr = 50; handled = FALSE; if( ev->direction == GDK_SCROLL_UP || ev->direction == GDK_SCROLL_DOWN ) { if( ev->state & GDK_CONTROL_MASK ) { if( ev->direction == GDK_SCROLL_UP ) imagepresent_zoom_in_centre( ip ); else imagepresent_zoom_out( ip ); handled = TRUE; } else if( ev->state & GDK_SHIFT_MASK ) { if( ev->direction == GDK_SCROLL_UP ) imagepresent_set_position( ip, visible->left + incr, visible->top ); else imagepresent_set_position( ip, visible->left - incr, visible->top ); handled = TRUE; } else { if( ev->direction == GDK_SCROLL_UP ) imagepresent_set_position( ip, visible->left, visible->top - incr ); else imagepresent_set_position( ip, visible->left, visible->top + incr ); handled = TRUE; } } return( handled ); } /* Our adjustments have changed (scroll or resize). */ static void imagepresent_hadj_changed_cb( GtkAdjustment *adj, Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; imagemodel->visible.left = adj->value; imagemodel->visible.width = adj->page_size; /* Update the visible hint on the conversion. */ conv->visible = imagemodel->visible; #ifdef DEBUG printf( "imagepresent_hadj_changed_cb: left = %d, width = %d\n", imagemodel->visible.left, imagemodel->visible.width ); #endif /*DEBUG*/ imagepresent_hruler_rethink( ip ); } static void imagepresent_vadj_changed_cb( GtkAdjustment *adj, Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; imagemodel->visible.top = adj->value; imagemodel->visible.height = adj->page_size; /* Update the visible hint on the conversion. */ conv->visible = imagemodel->visible; #ifdef DEBUG printf( "imagepresent_vadj_changed_cb: top = %d, height = %d\n", imagemodel->visible.top, imagemodel->visible.height ); #endif /*DEBUG*/ imagepresent_vruler_rethink( ip ); } static void imagepresent_floating_new( Imagepresent *ip, int left, int top, int width, int height, gboolean frozen, RegionviewType type, RegionviewResize resize, int x, int y ) { g_assert( !ip->regionview ); ip->floating.left = left; ip->floating.top = top; ip->floating.width = width; ip->floating.height = height; ip->regionview = regionview_new( NULL, &ip->floating, ip ); ip->regionview->frozen = frozen; ip->regionview->type = type; ip->regionview->resize = resize; regionview_attach( ip->regionview, x, y ); } /* Need to fwd ref this. */ static void imagepresent_left_release( Imagepresent *ip, GdkEvent *ev, int x, int y ); static gint imagepresent_hruler_event( GtkWidget *widget, GdkEvent *ev, Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; IMAGE *im = imageinfo_get( FALSE, conv->ii ); gboolean handled = FALSE; switch( ev->type ) { case GDK_BUTTON_PRESS: switch( ev->button.button ) { case 1: (void) imagemodel_set_state( imagemodel, IMAGEMODEL_SELECT, NULL ); imagepresent_floating_new( ip, 0, 0, im->Xsize, 0, TRUE, REGIONVIEW_HGUIDE, REGIONVIEW_RESIZE_BOTTOM, ev->button.x, ev->button.y ); /* The pointer will be grabbed for the drag on the * ruler window. We want to track in the main image * display window, so we have to explicitly ungrab. */ gdk_pointer_ungrab( ev->button.time ); handled = TRUE; break; default: break; } break; case GDK_BUTTON_RELEASE: switch( ev->button.button ) { case 1: imagepresent_left_release( ip, ev, ev->button.x, ev->button.y ); handled = TRUE; break; default: break; } break; default: break; } return( handled ); } static gint imagepresent_vruler_event( GtkWidget *widget, GdkEvent *ev, Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; IMAGE *im = imageinfo_get( FALSE, conv->ii ); gboolean handled = FALSE; switch( ev->type ) { case GDK_BUTTON_PRESS: switch( ev->button.button ) { case 1: (void) imagemodel_set_state( imagemodel, IMAGEMODEL_SELECT, NULL ); imagepresent_floating_new( ip, 0, 0, 0, im->Ysize, TRUE, REGIONVIEW_VGUIDE, REGIONVIEW_RESIZE_RIGHT, ev->button.x, ev->button.y ); gdk_pointer_ungrab( ev->button.time ); handled = TRUE; break; default: break; } break; case GDK_BUTTON_RELEASE: switch( ev->button.button ) { case 1: imagepresent_left_release( ip, ev, ev->button.x, ev->button.y ); handled = TRUE; break; default: break; } break; default: break; } return( handled ); } /* Track this during a snap. */ typedef struct { Imagepresent *ip; int x; /* Start point */ int y; int off_x; /* Current snap offset */ int off_y; int best_x; /* 'Closeness' of best snap so far */ int best_y; } ImagepresentSnap; static void * imagepresent_snap_sub( Regionview *regionview, ImagepresentSnap *snap, gboolean *snapped ) { Imagemodel *imagemodel = snap->ip->imagemodel; Conversion *conv = imagemodel->conv; Rect area; /* Only static h/v guides. */ if( regionview->type != REGIONVIEW_HGUIDE && regionview->type != REGIONVIEW_VGUIDE ) return( NULL ); if( regionview->state != REGIONVIEW_WAIT ) return( NULL ); /* Work in display cods. */ conversion_im_to_disp_rect( conv, ®ionview->area, &area ); if( regionview->type == REGIONVIEW_HGUIDE ) { int score = abs( area.top - snap->y ); if( score < snap->best_y ) { snap->off_y = area.top - snap->y; snap->best_y = score; *snapped = TRUE; } } else { int score = abs( area.left - snap->x ); if( score < snap->best_x ) { snap->off_x = area.left - snap->x; snap->best_x = score; *snapped = TRUE; } } return( NULL ); } static gboolean imagepresent_snap( Imagepresent *ip, ImagepresentSnap *snap ) { gboolean snapped; snap->ip = ip; snap->off_x = 0; snap->off_y = 0; snap->best_x = imagepresent_snap_threshold; snap->best_y = imagepresent_snap_threshold; snapped = FALSE; slist_map2( ip->regionviews, (SListMap2Fn) imagepresent_snap_sub, snap, &snapped ); return( snapped ); } gboolean imagepresent_snap_point( Imagepresent *ip, int x, int y, int *sx, int *sy ) { ImagepresentSnap snap; gboolean snapped; snap.x = x; snap.y = y; snapped = imagepresent_snap( ip, &snap ); *sx = x + snap.off_x; *sy = y + snap.off_y; return( snapped ); } gboolean imagepresent_snap_rect( Imagepresent *ip, Rect *in, Rect *out ) { ImagepresentSnap snap[8]; int i, best, best_score; gboolean snapped; /* Snap the corners plus the edge centres, take the best score. */ snap[0].x = in->left; snap[0].y = in->top; snap[1].x = in->left + in->width; snap[1].y = in->top; snap[2].x = in->left + in->width; snap[2].y = in->top + in->height; snap[3].x = in->left; snap[3].y = in->top + in->height; snap[4].x = in->left + in->width / 2; snap[4].y = in->top; snap[5].x = in->left + in->width; snap[5].y = in->top + in->height / 2; snap[6].x = in->left + in->width / 2; snap[6].y = in->top + in->height; snap[7].x = in->left; snap[7].y = in->top + in->height / 2; for( snapped = FALSE, i = 0; i < 8; i++ ) snapped |= imagepresent_snap( ip, &snap[i] ); best = 0; best_score = snap[0].best_x; for( i = 1; i < 7; i++ ) if( snap[i].best_x < best_score ) { best = i; best_score = snap[i].best_x; } out->left = in->left + snap[best].off_x; best = 0; best_score = snap[0].best_y; for( i = 1; i < 7; i++ ) if( snap[i].best_y < best_score ) { best = i; best_score = snap[i].best_y; } out->top = in->top + snap[best].off_y; out->width = in->width; out->height = in->height; return( snapped ); } /* Set position x, y in canvas coordinates as the top left of the window. */ void imagepresent_set_position( Imagepresent *ip, int x, int y ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; int maxx = conv->canvas.width - imagemodel->visible.width; int maxy = conv->canvas.height - imagemodel->visible.height; #ifdef DEBUG printf( "imagepresent_set_position: %d x %d\n", x, y ); #endif /*DEBUG*/ adjustments_set_value( ip->hadj, ip->vadj, IM_CLIP( 0, x, maxx ), IM_CLIP( 0, y, maxy ) ); } /* Set a new magnification, and scroll for the passed x/y position in image * coordinates to be in the centre of the screen. */ void imagepresent_set_mag_pos( Imagepresent *ip, int mag, int ix, int iy ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; int nx, ny; int last_x, last_y; #ifdef DEBUG printf( "imagepresent_set_mag_pos: %d, %d x %d\n", mag, ix, iy ); #endif /*DEBUG*/ /* Need to update last_x/y as well ... go to image cods around zoom * operation. */ conversion_disp_to_im( conv, ip->last_x, ip->last_y, &last_x, &last_y ); /* Take mouse pos to image cods around zoom operation. */ conversion_set_mag( conv, mag ); conversion_im_to_disp( conv, ix, iy, &nx, &ny ); /* ... and try to get that point in the centre of the window. We need * to zap in the new adjustment upper value, since this won't * otherwise get set until we get back to idle. */ ip->hadj->upper = conv->canvas.width; ip->vadj->upper = conv->canvas.height; imagepresent_set_position( ip, nx - imagemodel->visible.width / 2, ny - imagemodel->visible.height / 2 ); conversion_im_to_disp( conv, last_x, last_y, &ip->last_x, &ip->last_y ); } /* Set a magnification, keeping the centre of the screen in the centre. */ void imagepresent_zoom_to( Imagepresent *ip, int mag ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; /* If window is larger than image. */ int w = IM_MIN( imagemodel->visible.width, conv->canvas.width ); int h = IM_MIN( imagemodel->visible.height, conv->canvas.height ); int ix, iy; conversion_disp_to_im( conv, imagemodel->visible.left + w / 2, imagemodel->visible.top + h / 2, &ix, &iy ); imagepresent_set_mag_pos( ip, mag, ix, iy ); } /* Left button press event. */ static gboolean imagepresent_left_press( Imagepresent *ip, GdkEvent *ev, int x, int y ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; gboolean handled = FALSE; IMAGE *im2; int ix, iy; /* If there's a regionview grabbed already, block other actions. This * can happen with, for example, the win32 backend where we don't * always see a RELEASE for every PRESS. */ if( ip->regionview ) return( FALSE ); switch( imagemodel->state ) { case IMAGEMODEL_SELECT: if( ev->button.state & GDK_CONTROL_MASK ) { imagepresent_snap_point( ip, x, y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); imagepresent_floating_new( ip, ix, iy, 0, 0, FALSE, REGIONVIEW_MARK, REGIONVIEW_RESIZE_BOTTOMRIGHT, x, y ); handled = TRUE; } break; case IMAGEMODEL_PAN: /* Save how much we have to add to x_root to get x. */ ip->dx = ev->button.x_root + imagemodel->visible.left; ip->dy = ev->button.y_root + imagemodel->visible.top; break; case IMAGEMODEL_MAGIN: imagepresent_zoom_in( ip, x, y ); handled = TRUE; break; case IMAGEMODEL_MAGOUT: imagepresent_zoom_out( ip ); handled = TRUE; break; case IMAGEMODEL_DROPPER: case IMAGEMODEL_FLOOD: case IMAGEMODEL_BLOB: break; case IMAGEMODEL_PEN: case IMAGEMODEL_SMUDGE: imagepresent_snap_point( ip, x, y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); ip->paint_last_x = ix; ip->paint_last_y = iy; handled = TRUE; /* This can take ages and, via progress, actually process a * few events. Do it at the end. */ imagemodel_refresh_nib( imagemodel ); break; case IMAGEMODEL_LINE: imagepresent_snap_point( ip, x, y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); ip->paint_last_x = ix; ip->paint_last_y = iy; imagepresent_floating_new( ip, ix, iy, 0, 0, TRUE, REGIONVIEW_LINE, REGIONVIEW_RESIZE_BOTTOMRIGHT, x, y ); handled = TRUE; /* This can take ages and, via progress, actually process a * few events. Do it at the end. */ imagemodel_refresh_nib( imagemodel ); break; case IMAGEMODEL_RECT: imagepresent_snap_point( ip, x, y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); imagepresent_floating_new( ip, ix, iy, 0, 0, TRUE, REGIONVIEW_BOX, REGIONVIEW_RESIZE_BOTTOMRIGHT, x, y ); handled = TRUE; break; case IMAGEMODEL_TEXT: imagepresent_snap_point( ip, x, y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); ip->paint_last_x = ix; ip->paint_last_y = iy; if( !imagemodel_refresh_text( imagemodel ) ) { iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); break; } im2 = imageinfo_get( FALSE, imagemodel->text_mask ); imagepresent_floating_new( ip, ix, iy + imagemodel->text_area.top, im2->Xsize, im2->Ysize, TRUE, REGIONVIEW_BOX, REGIONVIEW_RESIZE_EDIT, x, y ); handled = TRUE; break; default: break; } return( handled ); } static void imagepresent_paint_stop( Imagepresent *ip, int x, int y ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; Imageinfo *imageinfo = conv->ii; Rect oper; int ix, iy; imagepresent_snap_point( ip, x, y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); switch( imagemodel->state ) { case IMAGEMODEL_DROPPER: if( im_rect_includespoint( &conv->underlay, ix, iy ) ) if( !imageinfo_paint_dropper( imageinfo, imagemodel->ink, ix, iy ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); break; case IMAGEMODEL_PEN: if( !imageinfo_paint_line( imageinfo, imagemodel->ink, imagemodel->nib, ip->paint_last_x, ip->paint_last_y, ix, iy ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); break; case IMAGEMODEL_LINE: if( ip->regionview ) { DESTROY_GTK( ip->regionview ); if( !imageinfo_paint_line( imageinfo, imagemodel->ink, imagemodel->nib, ip->floating.left, ip->floating.top, IM_RECT_RIGHT( &ip->floating ), IM_RECT_BOTTOM( &ip->floating ) ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); } break; case IMAGEMODEL_RECT: if( ip->regionview ) { DESTROY_GTK( ip->regionview ); im_rect_normalise( &ip->floating ); if( !imageinfo_paint_rect( imageinfo, imagemodel->ink, &ip->floating ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); } break; case IMAGEMODEL_FLOOD: if( !imageinfo_paint_flood( imageinfo, imagemodel->ink, ix, iy, FALSE ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); break; case IMAGEMODEL_BLOB: if( !imageinfo_paint_flood( imageinfo, imagemodel->ink, ix, iy, TRUE ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); break; case IMAGEMODEL_TEXT: if( ip->regionview ) { DESTROY_GTK( ip->regionview ); if( !imageinfo_paint_mask( imageinfo, imagemodel->ink, imagemodel->text_mask, ip->floating.left, ip->floating.top ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); } break; case IMAGEMODEL_SMUDGE: /* Area to smudge in display cods. */ oper.left = -10; oper.top = -10; oper.width = 20; oper.height = 20; /* Translate to IMAGE cods. */ conversion_disp_to_im_rect( conv, &oper, &oper ); if( !imageinfo_paint_smudge( imageinfo, &oper, ip->paint_last_x, ip->paint_last_y, ix, iy ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); break; default: break; } imagemodel_paint_recalc( imagemodel ); imageinfo_undo_mark( imageinfo ); /* Ask everyone to drop cache, the image has changed. */ im_invalidate( imageinfo_get( FALSE, imageinfo ) ); } /* Left button release event. */ static void imagepresent_left_release( Imagepresent *ip, GdkEvent *ev, int x, int y ) { Imagemodel *imagemodel = ip->imagemodel; Row *row = imagemodel->iimage ? HEAPMODEL( imagemodel->iimage )->row : NULL; switch( imagemodel->state ) { case IMAGEMODEL_SELECT: if( ip->regionview && row ) { /* Make a new region. */ char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); Symbol *sym; switch( ip->regionview->type ) { case REGIONVIEW_MARK: vips_buf_appendf( &buf, "%s ", CLASS_MARK ); row_qualified_name( row, &buf ); vips_buf_appendd( &buf, ip->floating.left ); vips_buf_appendd( &buf, ip->floating.top ); break; case REGIONVIEW_REGION: vips_buf_appendf( &buf, "%s ", CLASS_REGION ); row_qualified_name( row, &buf ); vips_buf_appendd( &buf, ip->floating.left ); vips_buf_appendd( &buf, ip->floating.top ); vips_buf_appendd( &buf, ip->floating.width ); vips_buf_appendd( &buf, ip->floating.height ); break; case REGIONVIEW_ARROW: vips_buf_appendf( &buf, "%s ", CLASS_ARROW ); row_qualified_name( row, &buf ); vips_buf_appendd( &buf, ip->floating.left ); vips_buf_appendd( &buf, ip->floating.top ); vips_buf_appendd( &buf, ip->floating.width ); vips_buf_appendd( &buf, ip->floating.height ); break; case REGIONVIEW_HGUIDE: vips_buf_appendf( &buf, "%s ", CLASS_HGUIDE ); row_qualified_name( row, &buf ); vips_buf_appendd( &buf, ip->floating.top ); break; case REGIONVIEW_VGUIDE: vips_buf_appendf( &buf, "%s ", CLASS_VGUIDE ); row_qualified_name( row, &buf ); vips_buf_appendd( &buf, ip->floating.left ); break; default: g_assert( FALSE ); } DESTROY_GTK( ip->regionview ); if( !(sym = workspace_add_def_recalc( row->ws, vips_buf_all( &buf ) )) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); workspace_deselect_all( row->ws ); } break; case IMAGEMODEL_DROPPER: case IMAGEMODEL_PEN: case IMAGEMODEL_LINE: case IMAGEMODEL_RECT: case IMAGEMODEL_FLOOD: case IMAGEMODEL_BLOB: case IMAGEMODEL_TEXT: case IMAGEMODEL_SMUDGE: imagepresent_paint_stop( ip, x, y ); break; default: break; } } /* Button motion event. */ static void imagepresent_button_motion( Imagepresent *ip, GdkEvent *ev ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; Imageinfo *imageinfo = conv->ii; Rect oper; int x, y; int ix, iy; imagepresent_snap_point( ip, ev->motion.x, ev->motion.y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); switch( imagemodel->state ) { case IMAGEMODEL_SELECT: break; case IMAGEMODEL_PAN: imagepresent_set_position( ip, (int) ip->dx - ev->motion.x_root, (int) ip->dy - ev->motion.y_root ); break; case IMAGEMODEL_MAGIN: break; case IMAGEMODEL_MAGOUT: break; case IMAGEMODEL_DROPPER: if( im_rect_includespoint( &conv->underlay, ix, iy ) ) if( !imageinfo_paint_dropper( imageinfo, imagemodel->ink, ix, iy ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); break; case IMAGEMODEL_PEN: if( !imageinfo_paint_line( imageinfo, imagemodel->ink, imagemodel->nib, ip->paint_last_x, ip->paint_last_y, ix, iy ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); im_invalidate( imageinfo_get( FALSE, imageinfo ) ); ip->paint_last_x = ix; ip->paint_last_y = iy; break; case IMAGEMODEL_LINE: /* rubberband */ break; case IMAGEMODEL_SMUDGE: /* Area to smudge in display cods. */ oper.left = -10; oper.top = -10; oper.width = 20; oper.height = 20; /* Translate to IMAGE cods. */ conversion_disp_to_im_rect( conv, &oper, &oper ); if( !imageinfo_paint_smudge( imageinfo, &oper, ip->paint_last_x, ip->paint_last_y, ix, iy ) ) iwindow_alert( GTK_WIDGET( ip ), GTK_MESSAGE_ERROR ); im_invalidate( imageinfo_get( FALSE, imageinfo ) ); ip->paint_last_x = ix; ip->paint_last_y = iy; break; default: break; } } /* Main event loop. */ static gboolean imagepresent_event_cb( GtkWidget *widget, GdkEvent *ev, Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; gboolean handled; #ifdef EVENT printf( "imagepresent_event_cb %d\n", ev->type ); #endif /*EVENT*/ handled = FALSE; switch( ev->type ) { case GDK_BUTTON_PRESS: if( !GTK_WIDGET_HAS_FOCUS( GTK_WIDGET( ip ) ) ) gtk_widget_grab_focus( GTK_WIDGET( ip ) ); switch( ev->button.button ) { case 1: handled = imagepresent_left_press( ip, ev, ev->button.x, ev->button.y ); break; case 2: #ifdef EVENT printf( "button2 press: at %gx%g\n", ev->button.x, ev->button.y ); #endif /*EVENT*/ /* Switch to pan, for this drag. */ imagemodel->save_state = imagemodel->state; (void) imagemodel_set_state( imagemodel, IMAGEMODEL_PAN, NULL ); handled = imagepresent_left_press( ip, ev, ev->button.x, ev->button.y ); break; default: break; } break; case GDK_BUTTON_RELEASE: switch( ev->button.button ) { case 1: imagepresent_left_release( ip, ev, ev->button.x, ev->button.y ); break; case 2: #ifdef EVENT printf( "button2 release: at %gx%g\n", ev->button.x, ev->button.y ); #endif /*EVENT*/ /* Should always succeed. */ (void) imagemodel_set_state( imagemodel, imagemodel->save_state, NULL ); break; default: break; } break; case GDK_MOTION_NOTIFY: /* We're using motion hints, so we need to read the pointer to * get the next one. */ widget_update_pointer( GTK_WIDGET( ip ), ev ); ip->last_x = ev->motion.x; ip->last_y = ev->motion.y; if( ev->motion.state & GDK_BUTTON1_MASK || ev->motion.state & GDK_BUTTON2_MASK ) imagepresent_button_motion( ip, ev ); /* Update tick marks on rulers, if they're being drawn. */ if( GTK_WIDGET_VISIBLE( ip->hrule ) ) { imagepresent_hruler_rethink( ip ); imagepresent_vruler_rethink( ip ); } break; case GDK_ENTER_NOTIFY: ip->inside = TRUE; break; case GDK_LEAVE_NOTIFY: ip->inside = FALSE; break; default: break; } return( handled ); } static gboolean imagepresent_key_press_event_cb( GtkWidget *widget, GdkEventKey *event, Imagepresent *ip ) { Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; Rect *visible = &imagemodel->visible; GtkAdjustment *hadj = ip->hadj; GtkAdjustment *vadj = ip->vadj; gboolean handled; int i; #ifdef DEBUG printf( "imagepresent_key_press_event_cb\n" ); #endif /*DEBUG*/ handled = FALSE; switch( event->keyval ) { case GDK_Left: if( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ) imagepresent_set_position( ip, visible->left - hadj->step_increment, visible->top ); else if( event->state & GDK_SHIFT_MASK ) imagepresent_set_position( ip, visible->left - hadj->page_increment, visible->top ); else if( event->state & GDK_CONTROL_MASK ) imagepresent_set_position( ip, 0, visible->top ); handled = TRUE; break; case GDK_Right: if( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ) imagepresent_set_position( ip, visible->left + hadj->step_increment, visible->top ); else if( event->state & GDK_SHIFT_MASK ) imagepresent_set_position( ip, visible->left + hadj->page_increment, visible->top ); else if( event->state & GDK_CONTROL_MASK ) imagepresent_set_position( ip, conv->canvas.width, visible->top ); handled = TRUE; break; case GDK_Up: if( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ) imagepresent_set_position( ip, visible->left, visible->top - vadj->step_increment ); else if( event->state & GDK_SHIFT_MASK ) imagepresent_set_position( ip, visible->left, visible->top - vadj->page_increment ); else if( event->state & GDK_CONTROL_MASK ) imagepresent_set_position( ip, visible->left, 0 ); handled = TRUE; break; case GDK_Down: if( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ) imagepresent_set_position( ip, visible->left, visible->top + vadj->step_increment ); else if( event->state & GDK_SHIFT_MASK ) imagepresent_set_position( ip, visible->left, visible->top + vadj->page_increment ); else if( event->state & GDK_CONTROL_MASK ) imagepresent_set_position( ip, visible->left, conv->canvas.height ); handled = TRUE; break; /* FIXME + and = are not always on the same key, of course :( */ case GDK_i: case GDK_plus: case GDK_equal: if( ip->inside ) imagepresent_zoom_in( ip, ip->last_x, ip->last_y ); else imagepresent_zoom_in_centre( ip ); handled = TRUE; break; case GDK_o: case GDK_minus: imagepresent_zoom_out( ip ); handled = TRUE; break; case GDK_0: conversion_set_mag( conv, 0 ); handled = TRUE; break; default: break; } /* Check the number zoom keys too. */ if( !handled ) for( i = 0; i < IM_NUMBER( imagepresent_keymap ); i++ ) if( event->keyval == imagepresent_keymap[i].keyval ) { int mask = event->state & GDK_CONTROL_MASK; int zoom = imagepresent_keymap[i].zoom; imagepresent_zoom_to( ip, mask ? -zoom : zoom ); handled = TRUE; break; } return( handled ); } /* ... and set the work window once that's there. */ static void imagepresent_realize_id_cb( Imagedisplay *id ) { iWindow *iwnd = IWINDOW( gtk_widget_get_toplevel( GTK_WIDGET( id ) ) ); iwindow_set_work_window( iwnd, GTK_WIDGET( id )->window ); } static void imagepresent_rulers_mm_cb( GtkWidget *wid, GtkWidget *host, Imagepresent *ip ) { ip->imagemodel->rulers_mm = gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM( wid ) ); iobject_changed( IOBJECT( ip->imagemodel ) ); } static void imagepresent_rulers_offset_cb( GtkWidget *wid, GtkWidget *host, Imagepresent *ip ) { ip->imagemodel->rulers_offset = gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM( wid ) ); iobject_changed( IOBJECT( ip->imagemodel ) ); } static void imagepresent_ruler_hide_cb( GtkWidget *wid, GtkWidget *host, Imagepresent *ip ) { imagemodel_set_rulers( ip->imagemodel, FALSE ); } static void imagepresent_init( Imagepresent *ip ) { GtkWidget *bar; GtkWidget *table; /* Basic init. */ ip->imagemodel = NULL; ip->dx = 0; ip->dy = 0; ip->last_x = 0; ip->last_y = 0; ip->inside = FALSE; ip->scroll_tid = 0; ip->u = 0; ip->v = 0; ip->regionview = NULL; ip->paint_last_x = 0; ip->paint_last_y = 0; ip->regionviews = NULL; ip->grabbed = NULL; /* Make main imagedisplay table. */ table = GTK_WIDGET( gtk_table_new( 2, 2, FALSE ) ); gtk_container_add( GTK_CONTAINER( ip ), table ); gtk_widget_show( table ); /* Make canvas. */ ip->id = imagedisplay_new( NULL ); GTK_WIDGET_SET_FLAGS( ip, GTK_CAN_FOCUS ); gtk_signal_connect( GTK_OBJECT( ip->id ), "realize", GTK_SIGNAL_FUNC( imagepresent_realize_id_cb ), NULL ); /* Press/release/motion-notify stuff. */ gtk_widget_add_events( GTK_WIDGET( ip->id ), GDK_KEY_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ); gtk_signal_connect_after( GTK_OBJECT( ip->id ), "event", GTK_SIGNAL_FUNC( imagepresent_event_cb ), ip ); gtk_signal_connect( GTK_OBJECT( ip ), "key_press_event", GTK_SIGNAL_FUNC( imagepresent_key_press_event_cb ), ip ); ip->swin = GTK_SCROLLED_WINDOW( gtk_scrolled_window_new( NULL, NULL ) ); gtk_scrolled_window_add_with_viewport( ip->swin, GTK_WIDGET( ip->id ) ); gtk_scrolled_window_set_policy( ip->swin, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); ip->hadj = gtk_scrolled_window_get_hadjustment( ip->swin ); ip->vadj = gtk_scrolled_window_get_vadjustment( ip->swin ); gtk_signal_connect( GTK_OBJECT( ip->swin ), "scroll_event", GTK_SIGNAL_FUNC( imagepresent_scroll_event_cb ), ip ); gtk_signal_connect( GTK_OBJECT( ip->hadj ), "changed", GTK_SIGNAL_FUNC( imagepresent_hadj_changed_cb ), ip ); gtk_signal_connect( GTK_OBJECT( ip->hadj ), "value_changed", GTK_SIGNAL_FUNC( imagepresent_hadj_changed_cb ), ip ); gtk_signal_connect( GTK_OBJECT( ip->vadj ), "changed", GTK_SIGNAL_FUNC( imagepresent_vadj_changed_cb ), ip ); gtk_signal_connect( GTK_OBJECT( ip->vadj ), "value_changed", GTK_SIGNAL_FUNC( imagepresent_vadj_changed_cb ), ip ); bar = ip->swin->hscrollbar; g_assert( GTK_IS_SCROLLBAR( bar ) ); GTK_WIDGET_UNSET_FLAGS( bar, GTK_CAN_FOCUS ); bar = ip->swin->vscrollbar; g_assert( GTK_IS_SCROLLBAR( bar ) ); GTK_WIDGET_UNSET_FLAGS( bar, GTK_CAN_FOCUS ); /* Need one menu per image window (could have a single menu for all * windows, but then we'd have to set the state of the toggle buttons * before mapping) */ ip->ruler_menu = popup_build( _( "Ruler menu" ) ); popup_add_tog( ip->ruler_menu, _( "Rulers In _mm" ), POPUP_FUNC( imagepresent_rulers_mm_cb ) ); popup_add_tog( ip->ruler_menu, _( "Show _Offset" ), POPUP_FUNC( imagepresent_rulers_offset_cb ) ); menu_add_sep( ip->ruler_menu ); popup_add_but( ip->ruler_menu, GTK_STOCK_CLOSE, POPUP_FUNC( imagepresent_ruler_hide_cb ) ); /* Make rulers. */ ip->hrule = GTK_HRULER( gtk_hruler_new() ); gtk_ruler_set_metric( GTK_RULER( ip->hrule ), GTK_PIXELS ); GTK_WIDGET_UNSET_FLAGS( GTK_WIDGET( ip->hrule ), GTK_CAN_FOCUS ); gtk_widget_show( GTK_WIDGET( ip->hrule ) ); ip->vrule = GTK_VRULER( gtk_vruler_new() ); gtk_ruler_set_metric( GTK_RULER( ip->vrule ), GTK_PIXELS ); GTK_WIDGET_UNSET_FLAGS( GTK_WIDGET( ip->vrule ), GTK_CAN_FOCUS ); gtk_widget_show( GTK_WIDGET( ip->vrule ) ); ip->heb = GTK_EVENT_BOX( gtk_event_box_new() ); gtk_container_add( GTK_CONTAINER( ip->heb ), GTK_WIDGET( ip->hrule ) ); gtk_signal_connect( GTK_OBJECT( ip->heb ), "event", GTK_SIGNAL_FUNC( imagepresent_hruler_event ), ip ); popup_attach( GTK_WIDGET( ip->heb ), ip->ruler_menu, ip ); ip->veb = GTK_EVENT_BOX( gtk_event_box_new() ); gtk_container_add( GTK_CONTAINER( ip->veb ), GTK_WIDGET( ip->vrule ) ); gtk_signal_connect( GTK_OBJECT( ip->veb ), "event", GTK_SIGNAL_FUNC( imagepresent_vruler_event ), ip ); popup_attach( GTK_WIDGET( ip->veb ), ip->ruler_menu, ip ); /* Attach all widgets to table. */ gtk_table_attach( GTK_TABLE( table ), GTK_WIDGET( ip->heb ), 1, 2, 0, 1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 2, 2 ); gtk_table_attach( GTK_TABLE( table ), GTK_WIDGET( ip->veb ), 0, 1, 1, 2, GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 2, 2 ); gtk_table_attach( GTK_TABLE( table ), GTK_WIDGET( ip->swin ), 1, 2, 1, 2, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 2, 2 ); gtk_widget_show( GTK_WIDGET( ip->id ) ); gtk_widget_show( GTK_WIDGET( ip->swin ) ); /* Set initial ruler visibility on first refresh from imagemodel. */ } GType imagepresent_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ImagepresentClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) imagepresent_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Imagepresent ), 32, /* n_preallocs */ (GInstanceInitFunc) imagepresent_init, }; type = g_type_register_static( GTK_TYPE_BIN, "Imagepresent", &info, 0 ); } return( type ); } /* The model has changed ... update! */ static void imagepresent_imagemodel_changed_cb( Imagemodel *imagemodel, Imagepresent *ip ) { if( ip->cntxt ) iwindow_cursor_context_set_cursor( ip->cntxt, imagepresent_cursors[imagemodel->state] ); widget_visible( GTK_WIDGET( ip->heb ), imagemodel->show_rulers ); widget_visible( GTK_WIDGET( ip->veb ), imagemodel->show_rulers ); imagepresent_hruler_rethink( ip ); imagepresent_vruler_rethink( ip ); } /* The model has a new imageinfo. */ static void imagepresent_imagemodel_imageinfo_changed_cb( Imagemodel *imagemodel, Imagepresent *ip ) { /* Reset our mode. We don't want to stay painting. */ if( imagemodel_state_paint( imagemodel->state ) ) imagemodel_set_state( imagemodel, IMAGEMODEL_SELECT, NULL ); } static void imagepresent_link( Imagepresent *ip, Imagemodel *imagemodel ) { ip->imagemodel = imagemodel; g_object_ref( G_OBJECT( ip->imagemodel ) ); iobject_sink( IOBJECT( ip->imagemodel ) ); g_signal_connect( G_OBJECT( imagemodel ), "changed", G_CALLBACK( imagepresent_imagemodel_changed_cb ), ip ); g_signal_connect( G_OBJECT( imagemodel ), "imageinfo_changed", G_CALLBACK( imagepresent_imagemodel_imageinfo_changed_cb ), ip ); imagedisplay_set_conversion( ip->id, imagemodel->conv ); if( imagemodel->iimage ) imagemodel->iimage->views = g_slist_prepend( imagemodel->iimage->views, ip ); } /* Make a new Imagepresent. */ Imagepresent * imagepresent_new( Imagemodel *imagemodel ) { Imagepresent *ip = g_object_new( TYPE_IMAGEPRESENT, NULL ); imagepresent_link( ip, imagemodel ); return( ip ); } /* Background scroller. */ static gboolean imagepresent_scroll_cb( Imagepresent *ip ) { imagepresent_set_position( ip, ip->imagemodel->visible.left + ip->u, ip->imagemodel->visible.top + ip->v ); return( TRUE ); } void imagepresent_scroll_start( Imagepresent *ip, int u, int v ) { if( !ip->scroll_tid ) ip->scroll_tid = g_timeout_add( 100, (GSourceFunc) imagepresent_scroll_cb, ip ); ip->u = u; ip->v = v; } void imagepresent_scroll_stop( Imagepresent *ip ) { IM_FREEF( g_source_remove, ip->scroll_tid ); ip->u = 0; ip->v = 0; } ================================================ FILE: src/imagepresent.h ================================================ /* Imagepresent widget stuff. */ /* Copyright (C) 1991-2001 The Natoinal 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IMAGEPRESENT (imagepresent_get_type()) #define IMAGEPRESENT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IMAGEPRESENT, Imagepresent )) #define IMAGEPRESENT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_IMAGEPRESENT, ImagepresentClass)) #define IS_IMAGEPRESENT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IMAGEPRESENT )) #define IS_IMAGEPRESENT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IMAGEPRESENT )) #define IMAGEPRESENT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_IMAGEPRESENT, ImagepresentClass )) /* Track an image view canvas in one of these. */ struct _Imagepresent { GtkBin parent_object; /* Context. */ Imagemodel *imagemodel; /* Keep model parts of widgets here */ /* Sub-widgets. */ Imagedisplay *id; /* Image we display */ GtkScrolledWindow *swin; GtkAdjustment *hadj; GtkAdjustment *vadj; GtkHRuler *hrule; /* Rulers */ GtkVRuler *vrule; GtkEventBox *heb; /* EventBoxes holding rulers */ GtkEventBox *veb; iWindowCursorContext *cntxt; GtkWidget *ruler_menu; /* Panning stuff. */ guint dx, dy; /* Drag start position */ /* Last known mouse position, mouse in window. */ int last_x, last_y; gboolean inside; /* Background scroll stuff. */ guint scroll_tid; int u, v; /* Rubberbanding. */ Regionview *regionview; /* region rubberband display */ Rect floating; /* rubberband area */ /* Painting stuff. */ int paint_last_x; int paint_last_y; /* Regionviews drawing on us. Used for snap-to-guide stuff. */ GSList *regionviews; /* The regionview that's currently grabbed ... maintained for us by * regionview.c ... see regionview_attach()/_detach() */ Regionview *grabbed; }; /* Class structure. */ typedef struct _ImagepresentClass { /* Our parent. */ GtkBinClass parent_class; } ImagepresentClass; gboolean imagepresent_snap_point( Imagepresent *ip, int x, int y, int *sx, int *sy ); gboolean imagepresent_snap_rect( Imagepresent *ip, Rect *in, Rect *out ); void imagepresent_paint_recalc( Imagepresent *ip ); GType imagepresent_get_type( void ); Imagepresent *imagepresent_new( Imagemodel *imagemodel ); void imagepresent_set_position( Imagepresent *ip, int x, int w ); void imagepresent_set_mag_pos( Imagepresent *ip, int mag, int ix, int iy ); void imagepresent_zoom_to( Imagepresent *ip, int mag ); void imagepresent_scroll_start( Imagepresent *ip, int u, int v ); void imagepresent_scroll_stop( Imagepresent *ip ); ================================================ FILE: src/imageview.c ================================================ /* display an image in a window ... watching an iImage model. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Define to trace button press events. #define EVENT */ #include "ip.h" static FloatwindowClass *parent_class = NULL; /* All the magnification menus we have. */ typedef struct _ImageviewMagmenu { const char *name; int mag; } ImageviewMagmenu; static const ImageviewMagmenu imageview_mags[] = { { "Zoom6Mode", -16 }, { "Zoom12Mode", -8 }, { "Zoom25Mode", -4 }, { "Zoom50Mode", -2 }, { "Zoom100Mode", 1 }, { "Zoom200Mode", 2 }, { "Zoom400Mode", 4 }, { "Zoom800Mode", 8 }, { "Zoom1600Mode", 16 } }; static void imageview_destroy( GtkObject *object ) { Imageview *iv; g_return_if_fail( object != NULL ); g_return_if_fail( IS_IMAGEVIEW( object ) ); iv = IMAGEVIEW( object ); #ifdef DEBUG printf( "imageview_destroy\n" ); #endif /*DEBUG*/ /* My instance destroy stuff. */ UNREF( iv->imagemodel ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void imageview_class_init( ImageviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = imageview_destroy; /* Create signals. */ /* Init methods. */ } static void imageview_init( Imageview *iv ) { iv->imagemodel = NULL; } GtkType imageview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Imageview", sizeof( Imageview ), sizeof( ImageviewClass ), (GtkClassInitFunc) imageview_class_init, (GtkObjectInitFunc) imageview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_FLOATWINDOW, &info ); } return( type ); } static void imageview_refresh_title( Imageview *iv ) { Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; Row *row = HEAPMODEL( iimage )->row; Workspace *ws = row_get_workspace( row ); /* Can come here during ws destroy. */ if( ws ) { Conversion *conv = imagemodel->conv; Imageinfo *ii = iimage->value.ii; char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); row_qualified_name_relative( ws->sym, row, &buf ); if( ii && imageinfo_is_from_file( ii ) ) vips_buf_appendf( &buf, " - %s", IOBJECT( ii )->name ); vips_buf_appendf( &buf, " - %.0f%%", 100.0 * conversion_dmag( conv->mag ) ); iwindow_set_title( IWINDOW( iv ), "%s", vips_buf_all( &buf ) ); } } /* The model has changed ... update our menus and titlebar. */ static void imageview_imagemodel_changed_cb( Imagemodel *imagemodel, Imageview *iv ) { iWindow *iwnd = IWINDOW( iv ); Conversion *conv = imagemodel->conv; GtkAction *action; int i; action = gtk_action_group_get_action( iwnd->action_group, "Status" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), imagemodel->show_status ); action = gtk_action_group_get_action( iwnd->action_group, "Control" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), imagemodel->show_convert ); action = gtk_action_group_get_action( iwnd->action_group, "Paint" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), imagemodel->show_paintbox ); action = gtk_action_group_get_action( iwnd->action_group, "Rulers" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), imagemodel->show_rulers ); for( i = 0; i < IM_NUMBER( imageview_mags ); i++ ) if( conv->mag == imageview_mags[i].mag ) { action = gtk_action_group_get_action( iwnd->action_group, imageview_mags[i].name ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), TRUE ); break; } imageview_refresh_title( iv ); } /* Region class names indexed by iRegionType. */ static const char *imageview_region_name[] = { CLASS_MARK, CLASS_HGUIDE, CLASS_VGUIDE, CLASS_ARROW, CLASS_REGION, CLASS_AREA }; /* Look up a iRegionType from an action name. */ static iRegionType imageview_get_region_type( GtkAction *action ) { /* Action names indexed by iRegionType. */ static const char *action_names[] = { "NewMark", "NewHGuide", "NewVGuide", "NewArrow", "NewRegion" }; const char *name = gtk_action_get_name( action ); int i; for( i = 0; i < IM_NUMBER( action_names ); i++ ) if( strcmp( name, action_names[i] ) == 0 ) return( (iRegionType) i ); g_assert( FALSE ); /* Keep gcc happy. */ return( FALSE ); } static void imageview_new_arrow2_action_cb( GtkAction *action, Imageview *iv ) { iRegionType rt = imageview_get_region_type( action ); Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; Row *row = HEAPMODEL( iimage )->row; Workspace *ws = row_get_workspace( row ); Conversion *conv = imagemodel->conv; int dx = imagemodel->visible.left + imagemodel->visible.width / 2; int dy = imagemodel->visible.top + imagemodel->visible.height / 2; char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); Symbol *sym; int ix, iy; conversion_disp_to_im( conv, dx, dy, &ix, &iy ); vips_buf_appendf( &buf, "%s ", imageview_region_name[rt] ); row_qualified_name_relative( ws->sym, row, &buf ); switch( rt ) { case IREGION_MARK: vips_buf_appendf( &buf, " (%d) (%d)", ix, iy ); break; case IREGION_HGUIDE: vips_buf_appendf( &buf, " (%d)", iy ); break; case IREGION_VGUIDE: vips_buf_appendf( &buf, " (%d)", ix ); break; default: g_assert( FALSE ); } if( !(sym = workspace_add_def_recalc( ws, vips_buf_all( &buf ) )) ) { iwindow_alert( GTK_WIDGET( iv ), GTK_MESSAGE_ERROR ); return; } workspace_deselect_all( ws ); } static void imageview_new_arrow4_action_cb( GtkAction *action, Imageview *iv ) { iRegionType rt = imageview_get_region_type( action ); Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; Row *row = HEAPMODEL( iimage )->row; Workspace *ws = row_get_workspace( row ); Conversion *conv = imagemodel->conv; Rect dr, ir; char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); Symbol *sym; Column *col; dr.left = imagemodel->visible.left + imagemodel->visible.width / 4; dr.top = imagemodel->visible.top + imagemodel->visible.height / 4; dr.width = imagemodel->visible.width / 2; dr.height = imagemodel->visible.height / 2; conversion_disp_to_im_rect( conv, &dr, &ir ); vips_buf_appendf( &buf, "%s ", imageview_region_name[rt] ); row_qualified_name_relative( ws->sym, row, &buf ); vips_buf_appendf( &buf, " (%d) (%d) %d %d", ir.left, ir.top, ir.width, ir.height ); if( !(sym = workspace_add_def_recalc( ws, vips_buf_all( &buf ) )) ) { iwindow_alert( GTK_WIDGET( iv ), GTK_MESSAGE_ERROR ); return; } col = sym->expr->row->top_col; column_scrollto( col, MODEL_SCROLL_BOTTOM ); } static void imageview_replace_action_cb( GtkAction *action, Imageview *iv ) { Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; classmodel_graphic_replace( CLASSMODEL( iimage ), GTK_WIDGET( iv ) ); } static void imageview_save_action_cb( GtkAction *action, Imageview *iv ) { Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; classmodel_graphic_save( CLASSMODEL( iimage ), GTK_WIDGET( iv ) ); } static void imageview_recalc_action_cb( GtkAction *action, Imageview *iv ) { Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; Row *row = HEAPMODEL( iimage )->row; workspace_deselect_all( row->ws ); row_select( row ); if( !workspace_selected_recalc( row->ws ) ) iwindow_alert( GTK_WIDGET( iv ), GTK_MESSAGE_ERROR ); workspace_deselect_all( row->ws ); } static void imageview_header_action_cb( GtkAction *action, Imageview *iv ) { Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; iimage_header( GTK_WIDGET( iv ), MODEL( iimage ) ); } static void imageview_zoom_in_action_cb( GtkAction *action, Imageview *iv ) { Imagemodel *imagemodel = iv->imagemodel; Conversion *conv = imagemodel->conv; conversion_set_mag( conv, conversion_double( conv->mag ) ); } static void imageview_zoom_out_action_cb( GtkAction *action, Imageview *iv ) { Imagemodel *imagemodel = iv->imagemodel; Conversion *conv = imagemodel->conv; conversion_set_mag( conv, conversion_halve( conv->mag ) ); } static void imageview_zoom_100_action_cb( GtkAction *action, Imageview *iv ) { if( iv->ip ) imagepresent_zoom_to( iv->ip, 1 ); } static void imageview_zoom_fit_action_cb( GtkAction *action, Imageview *iv ) { imagepresent_zoom_to( iv->ip, 0 ); } static void imageview_show_status_action_cb( GtkToggleAction *action, Imageview *iv ) { imagemodel_set_status( iv->imagemodel, gtk_toggle_action_get_active( action ) ); } static void imageview_show_convert_action_cb( GtkToggleAction *action, Imageview *iv ) { imagemodel_set_convert( iv->imagemodel, gtk_toggle_action_get_active( action ) ); } static void imageview_show_paintbox_action_cb( GtkToggleAction *action, Imageview *iv ) { imagemodel_set_paintbox( iv->imagemodel, gtk_toggle_action_get_active( action ) ); } static void imageview_show_rulers_action_cb( GtkToggleAction *action, Imageview *iv ) { imagemodel_set_rulers( iv->imagemodel, gtk_toggle_action_get_active( action ) ); } static void imageview_mode_action_cb( GtkRadioAction *action, GtkRadioAction *current, Imageview *iv ) { ImagemodelState state = (ImagemodelState) gtk_radio_action_get_current_value( action ); imagemodel_set_state( iv->imagemodel, state, GTK_WIDGET( iv ) ); } static void imageview_mag_action_cb( GtkRadioAction *action, GtkRadioAction *current, Imageview *iv ) { if( iv->ip ) imagepresent_zoom_to( iv->ip, gtk_radio_action_get_current_value( action ) ); } /* Our actions. */ static GtkActionEntry imageview_actions[] = { /* Menu items. */ { "ViewToolbarMenu", NULL, "_Toolbar" }, { "ViewModeMenu", NULL, "M_ode" }, { "ViewZoomMenu", NULL, "_Zoom" }, /* Actions. */ { "NewMark", NULL, N_( "_Mark" ), NULL, N_( "Create a new mark" ), G_CALLBACK( imageview_new_arrow2_action_cb ) }, { "NewHGuide", NULL, N_( "_Horizontal Guide" ), NULL, N_( "Create a new horizontal guide" ), G_CALLBACK( imageview_new_arrow2_action_cb ) }, { "NewVGuide", NULL, N_( "_Vertical Guide" ), NULL, N_( "Create a new vertical guide" ), G_CALLBACK( imageview_new_arrow2_action_cb ) }, { "NewArrow", NULL, N_( "_Arrow" ), NULL, N_( "Create a new arrow" ), G_CALLBACK( imageview_new_arrow4_action_cb ) }, { "NewRegion", NULL, N_( "_Region" ), NULL, N_( "Create a new region" ), G_CALLBACK( imageview_new_arrow4_action_cb ) }, { "Replace", NULL, N_( "Replace Image" ), NULL, N_( "Replace image from file" ), G_CALLBACK( imageview_replace_action_cb ) }, { "SaveAs", GTK_STOCK_SAVE_AS, N_( "Save Image As" ), NULL, N_( "Save image to file" ), G_CALLBACK( imageview_save_action_cb ) }, { "Recalculate", NULL, N_( "Recalculate" ), "C", N_( "Recalculate image" ), G_CALLBACK( imageview_recalc_action_cb ) }, { "Header", NULL, N_( "_Header" ), NULL, N_( "View image header" ), G_CALLBACK( imageview_header_action_cb ) }, { "ZoomIn", GTK_STOCK_ZOOM_IN, N_( "Zoom _In" ), "plus", N_( "Zoom in on mouse cursor" ), G_CALLBACK( imageview_zoom_in_action_cb ) }, { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_( "Zoom _Out" ), "minus", N_( "Zoom out" ), G_CALLBACK( imageview_zoom_out_action_cb ) }, { "Zoom100", GTK_STOCK_ZOOM_100, N_( "Zoom _100%" ), "equal", N_( "Zoom to 100%" ), G_CALLBACK( imageview_zoom_100_action_cb ) }, { "ZoomFit", GTK_STOCK_ZOOM_FIT, N_( "Zoom to _Fit" ), NULL, N_( "Zoom to fit image to window" ), G_CALLBACK( imageview_zoom_fit_action_cb ) } }; static GtkToggleActionEntry imageview_toggle_actions[] = { { "Status", NULL, N_( "_Status" ), NULL, N_( "Show status bar" ), G_CALLBACK( imageview_show_status_action_cb ), TRUE }, { "Control", NULL, N_( "_Display Control" ), NULL, N_( "Show display control bar" ), G_CALLBACK( imageview_show_convert_action_cb ), TRUE }, { "Paint", NULL, N_( "_Paint" ), NULL, N_( "Show paint bar" ), G_CALLBACK( imageview_show_paintbox_action_cb ), FALSE }, { "Rulers", NULL, N_( "_Rulers" ), NULL, N_( "Show rulers" ), G_CALLBACK( imageview_show_rulers_action_cb ), FALSE } }; static GtkRadioActionEntry imageview_mode_radio_actions[] = { { "SelectMode", NULL, N_( "_Select" ), NULL, N_( "Select and modify selections" ), IMAGEMODEL_SELECT }, { "PanMode", NULL, N_( "_Pan" ), NULL, N_( "Pan image" ), IMAGEMODEL_PAN }, { "ZoomInMode", NULL, N_( "Zoom _In" ), NULL, N_( "Zoom in on mouse cursor" ), IMAGEMODEL_MAGIN }, { "ZoomOutMode", NULL, N_( "Zoom _Out" ), NULL, N_( "Zoom out" ), IMAGEMODEL_MAGOUT } }; static GtkRadioActionEntry imageview_zoom_radio_actions[] = { { "Zoom6Mode", NULL, N_( "6%" ), NULL, N_( "Zoom to 6%" ), -16 }, { "Zoom12Mode", NULL, N_( "12%" ), NULL, N_( "Zoom to 12%" ), -8 }, { "Zoom25Mode", NULL, N_( "25%" ), NULL, N_( "Zoom to 25%" ), -4 }, { "Zoom50Mode", NULL, N_( "50%" ), NULL, N_( "Zoom to 50%" ), -2 }, { "Zoom100Mode", NULL, N_( "100%" ), NULL, N_( "Zoom to 100%" ), 1 }, { "Zoom200Mode", NULL, N_( "200%" ), NULL, N_( "Zoom to 200%" ), 2 }, { "Zoom400Mode", NULL, N_( "400%" ), NULL, N_( "Zoom to 400%" ), 4 }, { "Zoom800Mode", NULL, N_( "800%" ), NULL, N_( "Zoom to 800%" ), 8 }, { "Zoom1600Mode", NULL, N_( "1600%" ), NULL, N_( "Zoom to 1600%" ), 16 } }; static const char *imageview_menubar_ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static gint imageview_event( GtkWidget *widget, GdkEvent *event, Imageview *iv ) { gboolean handled = FALSE; #ifdef EVENT if( event->type == GDK_BUTTON_PRESS ) printf( "imageview_event: GDK_BUTTON_PRESS\n" ); #endif /*EVENT*/ switch( event->type ) { case GDK_MOTION_NOTIFY: { Imagemodel *imagemodel = iv->imagemodel; Conversion *conv = imagemodel->conv; int ix, iy; conversion_disp_to_im( conv, event->button.x, event->button.y, &ix, &iy ); statusview_mouse( iv->sv, ix, iy ); } break; case GDK_BUTTON_PRESS: switch( event->button.button ) { case 3: { iWindow *iwnd = IWINDOW( iv ); GtkWidget *popup; popup = gtk_ui_manager_get_widget( iwnd->ui_manager, "/ImageviewPopup" ); gtk_menu_popup( GTK_MENU( popup ), NULL, NULL, (GtkMenuPositionFunc) NULL, NULL, 3, event->button.time ); handled = TRUE; } break; default: break; } break; default: break; } return( handled ); } static gboolean imageview_filedrop( Imageview *iv, const char *file ) { gboolean result; if( (result = iimage_replace( iv->imagemodel->iimage, file )) ) symbol_recalculate_all(); return( result ); } static void imageview_build( Imageview *iv, GtkWidget *vbox, iImage *iimage ) { iWindow *iwnd = IWINDOW( iv ); GError *error; GtkWidget *mbar; GtkWidget *frame; GList *focus_chain; /* All the model parts for our set of views. */ iv->imagemodel = imagemodel_new( iimage ); g_object_ref( G_OBJECT( iv->imagemodel ) ); iobject_sink( IOBJECT( iv->imagemodel ) ); iv->imagemodel_changed_sid = g_signal_connect( G_OBJECT( iv->imagemodel ), "changed", G_CALLBACK( imageview_imagemodel_changed_cb ), iv ); /* Make main menu bar */ gtk_action_group_add_actions( iwnd->action_group, imageview_actions, G_N_ELEMENTS( imageview_actions ), GTK_WINDOW( iv ) ); gtk_action_group_add_toggle_actions( iwnd->action_group, imageview_toggle_actions, G_N_ELEMENTS( imageview_toggle_actions ), GTK_WINDOW( iv ) ); gtk_action_group_add_radio_actions( iwnd->action_group, imageview_mode_radio_actions, G_N_ELEMENTS( imageview_mode_radio_actions ), IMAGEMODEL_SELECT, G_CALLBACK( imageview_mode_action_cb ), GTK_WINDOW( iv ) ); gtk_action_group_add_radio_actions( iwnd->action_group, imageview_zoom_radio_actions, G_N_ELEMENTS( imageview_zoom_radio_actions ), 1, G_CALLBACK( imageview_mag_action_cb ), GTK_WINDOW( iv ) ); error = NULL; if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager, imageview_menubar_ui_description, -1, &error ) ) { g_message( "building menus failed: %s", error->message ); g_error_free( error ); exit( EXIT_FAILURE ); } mbar = gtk_ui_manager_get_widget( iwnd->ui_manager, "/ImageviewMenubar" ); gtk_box_pack_start( GTK_BOX( vbox ), mbar, FALSE, FALSE, 0 ); gtk_widget_show( mbar ); /* This will set to NULL if we don't have infobar support. */ if( (IWINDOW( iv )->infobar = infobar_new()) ) gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( IWINDOW( iv )->infobar ), FALSE, FALSE, 0 ); /* Status bar. */ iv->sv = statusview_new( iv->imagemodel ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( iv->sv ), FALSE, FALSE, 0 ); /* Conversion bar. */ iv->cv = conversionview_new( iv->imagemodel ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( iv->cv ), FALSE, FALSE, 0 ); /* Paintbox bar. */ iv->pbv = paintboxview_new( iv->imagemodel ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( iv->pbv ), FALSE, FALSE, 0 ); /* Image area. */ frame = gtk_frame_new( NULL ); gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_OUT ); gtk_widget_show( frame ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 ); iv->ip = imagepresent_new( iv->imagemodel ); gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( iv->ip ) ); gtk_widget_show( GTK_WIDGET( iv->ip ) ); gtk_signal_connect_after( GTK_OBJECT( iv->ip->id ), "event", GTK_SIGNAL_FUNC( imageview_event ), iv ); /* Position and size to restore? */ if( MODEL( iimage )->window_width != -1 ) { /* Floatwindow will set pos/size. */ iv->imagemodel->show_status = iimage->show_status; iv->imagemodel->show_paintbox = iimage->show_paintbox; iv->imagemodel->show_convert = iimage->show_convert; iv->imagemodel->show_rulers = iimage->show_rulers; iv->imagemodel->scale = iimage->scale; iv->imagemodel->offset = iimage->offset; iv->imagemodel->falsecolour = iimage->falsecolour; iv->imagemodel->type = iimage->type; /* Our caller must call imagepresent_set_mag_pos() after * _show(). Not accurate if we set it here. */ } else { int w, h; /* Set initial size. This is really hard to do right :-( These * magic numbers will break with different themes. FIXME ... maybe realize the window but don't map it, calculate border size, then set default size and map? yuk! the magic numbers here are hard to derive, there are many, many widgets piled up together to make this window last set correctly for clearlooks */ w = IM_MIN( IMAGE_WINDOW_WIDTH, iv->imagemodel->conv->image.width + 14 ); h = IM_MIN( IMAGE_WINDOW_HEIGHT, iv->imagemodel->conv->image.height + 39 ); gtk_window_set_default_size( GTK_WINDOW( iv ), w, h ); conversion_set_mag( iv->imagemodel->conv, 1 ); } /* Set as file drop destination */ filedrop_register( GTK_WIDGET( iv ), (FiledropFunc) imageview_filedrop, iv ); /* Override the focus_chain ... we want the imagedisplay first. */ focus_chain = NULL; focus_chain = g_list_append( focus_chain, iv->ip ); focus_chain = g_list_append( focus_chain, iv->cv ); focus_chain = g_list_append( focus_chain, iv->pbv ); gtk_container_set_focus_chain( GTK_CONTAINER( vbox ), focus_chain ); g_list_free( focus_chain ); gtk_widget_grab_focus( GTK_WIDGET( iv->ip->id ) ); } static void * imageview_add_region( Classmodel *classmodel, Imageview *iv ) { iRegionInstance *instance; if( MODEL( classmodel )->display && (instance = classmodel_get_instance( classmodel )) ) { Regionview *regionview = regionview_new( classmodel, &instance->area, iv->ip ); PElement *root = &HEAPMODEL( classmodel )->row->expr->root; /* Look at the class we are drawing, set the display type. */ regionview_set_type( regionview, root ); } return( NULL ); } static void imageview_popdown( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Imageview *iv = IMAGEVIEW( iwnd ); Imagemodel *imagemodel = iv->imagemodel; iImage *iimage = imagemodel->iimage; Conversion *conv = imagemodel->conv; /* We have to note position/size in popdown rather than destroy, since * the widgets have to all still be extant. */ /* Save the centre of the window in image cods. */ conversion_disp_to_im( conv, imagemodel->visible.left + imagemodel->visible.width / 2, imagemodel->visible.top + imagemodel->visible.height / 2, &iimage->image_left, &iimage->image_top ); iimage->image_mag = conv->mag; iimage->show_status = imagemodel->show_status; iimage->show_paintbox = imagemodel->show_paintbox; iimage->show_rulers = imagemodel->show_rulers; /* Signal changed on iimage if we save the convert settings. This will * make the thumbnail update. */ if( iimage->show_convert != imagemodel->show_convert || iimage->scale != imagemodel->scale || iimage->offset != imagemodel->offset || iimage->falsecolour != imagemodel->falsecolour || iimage->type != imagemodel->type ) { iimage->show_convert = imagemodel->show_convert; iimage->scale = imagemodel->scale; iimage->offset = imagemodel->offset; iimage->falsecolour = imagemodel->falsecolour; iimage->type = imagemodel->type; iobject_changed( IOBJECT( iimage ) ); } nfn( sys, IWINDOW_YES ); } static void imageview_link( Imageview *iv, iImage *iimage, GtkWidget *parent ) { iwindow_set_build( IWINDOW( iv ), (iWindowBuildFn) imageview_build, iimage, NULL, NULL ); iwindow_set_popdown( IWINDOW( iv ), imageview_popdown, NULL ); iwindow_set_parent( IWINDOW( iv ), parent ); floatwindow_link( FLOATWINDOW( iv ), MODEL( iimage ) ); iwindow_build( IWINDOW( iv ) ); slist_map( iimage->classmodels, (SListMapFn) imageview_add_region, iv ); /* Initial "changed" on the model to get all views to init. */ iobject_changed( IOBJECT( iv->imagemodel ) ); } Imageview * imageview_new( iImage *iimage, GtkWidget *parent ) { Imageview *iv = gtk_type_new( TYPE_IMAGEVIEW ); imageview_link( iv, iimage, parent ); /* This is odd ... we wouldn't normally _show() the widget in _new(), * but restoring the scroll position doesn't work unless the window is * visible. We have to show here. */ gtk_widget_show( GTK_WIDGET( iv ) ); if( MODEL( iimage )->window_width != -1 ) imagepresent_set_mag_pos( iv->ip, iimage->image_mag, iimage->image_left, iimage->image_top ); return( iv ); } /* Make an imageview, and try to make area (image cods) visible. width/height * can be -ve */ Imageview * imageview_new_area( iImage *iimage, Rect *area, GtkWidget *parent ) { Imageview *iv = imageview_new( iimage, parent ); Imagemodel *imagemodel = iv->imagemodel; Conversion *conv = imagemodel->conv; int shrink_x, shrink_y, shrink; /* Calculate a shrink factor which should make all the region * visible ... don't zoom. */ shrink_x = (abs( area->width ) + conv->canvas.width) / conv->canvas.width; shrink_y = (abs( area->height ) + conv->canvas.height) / conv->canvas.height; shrink = -IM_MAX( 1, IM_MAX( shrink_x, shrink_y ) ); if( shrink == -1 ) shrink = 1; imagepresent_set_mag_pos( iv->ip, shrink, area->left + area->width / 2, area->top + area->height / 2 ); return( iv ); } ================================================ FILE: src/imageview.h ================================================ /* Decls for imageview.c ... display an image in a window. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IMAGEVIEW (imageview_get_type()) #define IMAGEVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_IMAGEVIEW, Imageview )) #define IMAGEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IMAGEVIEW, ImageviewClass )) #define IS_IMAGEVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_IMAGEVIEW )) #define IS_IMAGEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IMAGEVIEW )) typedef struct _Imageview { Floatwindow parent_class; /* Model stuff here. */ Imagemodel *imagemodel; guint imagemodel_changed_sid; Imagepresent *ip; Conversionview *cv; Statusview *sv; Paintboxview *pbv; } Imageview; typedef struct _ImageviewClass { FloatwindowClass parent_class; /* My methods. */ } ImageviewClass; GtkType imageview_get_type( void ); void imageview_set_paint( Imageview *iv, gboolean paint ); Imageview *imageview_new( iImage *iimage, GtkWidget *parent ); Imageview *imageview_new_area( iImage *iimage, Rect *area, GtkWidget *parent ); ================================================ FILE: src/iobject.c ================================================ /* abstract base class for all nip 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Our signals. */ enum { SIG_DESTROY, /* End lifetime */ SIG_CHANGED, /* iObject has changed somehow */ SIG_LAST }; static GObjectClass *parent_class = NULL; static guint iobject_signals[SIG_LAST] = { 0 }; /* Don't emit "destroy" immediately, do it from the _dispose handler. */ void * iobject_destroy( iObject *iobject ) { #ifdef DEBUG printf( "iobject_destroy: " ); iobject_print( iobject ); #endif /*DEBUG*/ if( !iobject->in_destruction ) g_object_run_dispose( G_OBJECT( iobject ) ); return( NULL ); } void * iobject_changed( iObject *iobject ) { g_return_val_if_fail( iobject != NULL, NULL ); g_return_val_if_fail( IS_IOBJECT( iobject ), NULL ); #ifdef DEBUG printf( "iobject_changed: " ); iobject_print( iobject ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( iobject ), iobject_signals[SIG_CHANGED], 0 ); return( NULL ); } void * iobject_info( iObject *iobject, VipsBuf *buf ) { iObjectClass *iobject_class = IOBJECT_GET_CLASS( iobject ); g_return_val_if_fail( iobject != NULL, NULL ); g_return_val_if_fail( IS_IOBJECT( iobject ), NULL ); if( iobject_class->info ) iobject_class->info( iobject, buf ); return( NULL ); } static void iobject_dispose( GObject *gobject ) { iObject *iobject = IOBJECT( gobject ); #ifdef DEBUG printf( "iobject_dispose: " ); iobject_print( iobject ); #endif /*DEBUG*/ if( !iobject->in_destruction ) { iobject->in_destruction = TRUE; g_signal_emit( G_OBJECT( iobject ), iobject_signals[SIG_DESTROY], 0 ); iobject->in_destruction = FALSE; } G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void iobject_finalize( GObject *gobject ) { iObject *iobject = IOBJECT( gobject ); #ifdef DEBUG printf( "iobject_finalize: " ); iobject_print( iobject ); #endif /*DEBUG*/ /* Unlike GTK, we allow floating objects to be finalized. Handy if a * _new() fails. So don't assert( !iobject->floating ); */ IM_FREE( iobject->name ); IM_FREE( iobject->caption ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void iobject_real_destroy( iObject *iobject ) { } static void iobject_real_changed( iObject *iobject ) { iObjectClass *iobject_class = IOBJECT_GET_CLASS( iobject ); if( iobject_class->generate_caption ) IM_SETSTR( iobject->caption, iobject_class->generate_caption( iobject ) ); } static void iobject_real_info( iObject *iobject, VipsBuf *buf ) { if( iobject->name ) vips_buf_appendf( buf, "name = \"%s\"\n", iobject->name ); if( iobject->caption ) vips_buf_appendf( buf, "caption = \"%s\"\n", iobject->caption ); vips_buf_appendf( buf, "iObject :: \"%s\"\n", G_OBJECT_TYPE_NAME( iobject ) ); } static void iobject_class_init( iObjectClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = iobject_dispose; gobject_class->finalize = iobject_finalize; class->destroy = iobject_real_destroy; class->changed = iobject_real_changed; class->info = iobject_real_info; class->generate_caption = NULL; class->user_name = _( "Object" ); /* Create signals. */ iobject_signals[SIG_DESTROY] = g_signal_new( "destroy", G_TYPE_FROM_CLASS( gobject_class ), G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET( iObjectClass, destroy ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); iobject_signals[SIG_CHANGED] = g_signal_new( "changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( iObjectClass, changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } static void iobject_init( iObject *iobject ) { #ifdef DEBUG printf( "iobject_init: " ); iobject_print( iobject ); #endif /*DEBUG*/ /* Init our instance fields. */ iobject->name = NULL; iobject->caption = NULL; iobject->floating = TRUE; iobject->in_destruction = FALSE; } GType iobject_get_type( void ) { static GType iobject_type = 0; if( !iobject_type ) { static const GTypeInfo info = { sizeof( iObjectClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) iobject_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( iObject ), 32, /* n_preallocs */ (GInstanceInitFunc) iobject_init, }; iobject_type = g_type_register_static( G_TYPE_OBJECT, "iObject", &info, 0 ); } return( iobject_type ); } /* Test the name field ... handy with map. */ void * iobject_test_name( iObject *iobject, const char *name ) { g_return_val_if_fail( iobject != NULL, NULL ); g_return_val_if_fail( IS_IOBJECT( iobject ), NULL ); if( iobject->name && strcmp( iobject->name, name ) == 0 ) return( iobject ); return( NULL ); } void * iobject_print( iObject *iobject ) { g_print( "%s \"%s\" (%p)\n", G_OBJECT_TYPE_NAME( iobject ), NN( iobject->name ), iobject ); return( NULL ); } void iobject_set( iObject *iobject, const char *name, const char *caption ) { gboolean changed = FALSE; g_return_if_fail( iobject != NULL ); g_return_if_fail( IS_IOBJECT( iobject ) ); if( name && name != iobject->name ) { IM_SETSTR( iobject->name, name ); changed = TRUE; } if( caption && caption != iobject->caption ) { IM_SETSTR( iobject->caption, caption ); changed = TRUE; } if( changed ) iobject_changed( iobject ); #ifdef DEBUG printf( "iobject_set: " ); iobject_print( iobject ); #endif /*DEBUG*/ } void iobject_sink( iObject *iobject ) { g_assert( IS_IOBJECT( iobject ) ); if( iobject->floating ) { iobject->floating = FALSE; g_object_unref( G_OBJECT( iobject ) ); } } void iobject_dump( iObject *iobject ) { char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); iobject_info( iobject, &buf ); printf( "%s", vips_buf_all( &buf ) ); } ================================================ FILE: src/iobject.h ================================================ /* abstract base class for all nip 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IOBJECT (iobject_get_type()) #define IOBJECT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IOBJECT, iObject )) #define IOBJECT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_IOBJECT, iObjectClass)) #define IS_IOBJECT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IOBJECT )) #define IS_IOBJECT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IOBJECT )) #define IOBJECT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_IOBJECT, iObjectClass )) /* Handy iobject_destroy() shortcut. */ #define IDESTROY( O ) { \ if( O ) { \ (void) iobject_destroy( IOBJECT( O ) ); \ ( O ) = NULL; \ } \ } struct _iObject { GObject parent_object; /* My instance vars. */ char *name; /* iObject name */ char *caption; /* Comment of some sort */ /* True when created ... the 1 reference that gobject makes is * 'floating' and not owned by anyone. Do _sink() after every _ref() * to transfer ownership to the parent container. Upshot: no need to * _unref() after _add() in _new(). */ gboolean floating; /* Stop destroy loops with this. */ gboolean in_destruction; }; typedef struct _iObjectClass { GObjectClass parent_class; /* End object's lifetime, just like gtk_object_destroy. */ void (*destroy)( iObject * ); /* Something about the object has changed. Should use glib's properties * but fix this later. */ void (*changed)( iObject * ); /* Try and say something useful about us. */ void (*info)( iObject *, VipsBuf * ); /* Called on _changed() to update the caption. Define this if you want * the caption to be an explanatory note about the object. */ const char *(*generate_caption)( iObject * ); /* The i18n name for this class we show the user. FOr example, * Workspace is referred to as "tab" by the user. */ const char *user_name; } iObjectClass; #define IOBJECT_GET_CLASS_NAME( obj ) \ ((G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_IOBJECT, iObjectClass ))->user_name) void *iobject_destroy( iObject *iobject ); void *iobject_changed( iObject *iobject ); void *iobject_info( iObject *iobject, VipsBuf * ); GType iobject_get_type( void ); void *iobject_test_name( iObject *iobject, const char *name ); void *iobject_print( iObject *iobject ); void iobject_set( iObject *iobject, const char *name, const char *caption ); void iobject_sink( iObject *iobject ); void iobject_dump( iObject *iobject ); ================================================ FILE: src/ip.h ================================================ /* All ip headers. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* We can get multiple includes sometimes, gah, thank you bison. */ #ifndef IP_H #define IP_H /* DEBUG everywhere. #define DEBUG */ /* Turn off VIPS's old and broken defines, we don't need them. */ #define IM_NO_VIPS7_COMPAT /* Enable heap sanity checks on every alloc ... very slow ... also see heap.c #define DEBUG_HEAP */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #ifdef ENABLE_NLS #include #define _(String) gettext(String) #ifdef gettext_noop #define N_(String) gettext_noop(String) #else #define N_(String) (String) #endif #else /* NLS is disabled */ #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)) #endif /* ENABLE_NLS */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PWD_H #include #endif /*HAVE_PWD_H*/ #ifdef HAVE_FNMATCH_H #include #endif /*HAVE_FNMATCH_H*/ #ifdef HAVE_SYS_PARAM_H #include #endif /*HAVE_SYS_PARAM_H*/ #include #ifdef HAVE_SYS_TIME_H #include #endif /*HAVE_SYS_TIME_H*/ #include #ifdef HAVE_SYS_RESOURCE_H #include #endif /*HAVE_SYS_RESOURCE_H*/ #ifdef HAVE_SYS_WAIT_H #include #endif /*HAVE_SYS_WAIT_H*/ #ifdef HAVE_UNISTD_H #include #endif /*HAVE_UNISTD_H*/ #ifdef HAVE_SYS_STATVFS_H #include #endif /*HAVE_SYS_STATVFS_H*/ #ifdef HAVE_SYS_VFS_H #include #endif /*HAVE_SYS_VFS_H*/ #ifdef HAVE_SYS_MOUNT_H #include #endif /*HAVE_SYS_MOUNT_H*/ #ifdef OS_WIN32 #include #endif /*OS_WIN32*/ #ifdef HAVE_FFTW #include #endif /*HAVE_FFTW*/ #ifdef HAVE_FFTW3 #include #endif /*HAVE_FFTW3*/ #include /* Have to include glib before dmalloc ... dmalloc may be included by vips.h */ #include #include #ifdef HAVE_LIBGOFFICE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /*HAVE_LIBGOFFICE*/ #ifdef HAVE_LIBGVC #include #endif /*HAVE_LIBGVC*/ #include #include #include #include #include /* If we're not using GNU C, elide __attribute__ */ #ifndef __GNUC__ # ifndef __attribute__ # define __attribute__(x) /*NOTHING*/ # endif #endif /* Our general widgets. */ #include "formula.h" #include "doubleclick.h" /* Generated marshallers. */ #include "nipmarshal.h" /* XML namespace ... note, not nip2! We can't change this. */ #define NAMESPACE "http://www.vips.ecs.soton.ac.uk/nip" #define MAXFILES (4000) /* Max. no of files in path */ #define STACK_SIZE (1000) /* Depth of eval stack */ #define LEN_LABEL (512) /* Label on windows */ #define MAX_SYSTEM (50) /* Max number of args we allow */ #define MAX_BANDS (64) /* Max number of bands in image */ #define MAX_CSTACK (10) /* Max number of cursors we stack */ #define MAX_STRSIZE (100000) /* Size of text for user defs */ #define MAX_TRACE (1024) /* Biggest thing we print in trace */ #define MAX_SSTACK (40) /* Scope stack for parser */ #define VIPS_HOMEPAGE "https://github.com/jcupitt/nip2" #define IP_NAME PACKAGE "-" VERSION #define NIP_DOCPATH "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S \ "doc" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "html" #define VIPS_DOCPATH "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S \ "doc" G_DIR_SEPARATOR_S "vips" G_DIR_SEPARATOR_S "html" #define IP_NAME PACKAGE "-" VERSION #define MAX_LINELENGTH (120) /* Max chars we display of value */ #define MAX_RECENT (10) /* Number of recent items in file menu */ #define NIP_COPYRIGHT "%s: ©2023 libvips.org" /* Our stock_ids. */ #define STOCK_NEXT_ERROR "nip-next-error" #define STOCK_DROPPER "nip-dropper" #define STOCK_DUPLICATE "nip-duplicate" #define STOCK_PAINTBRUSH "nip-paintbrush" #define STOCK_LINE "nip-linedraw" #define STOCK_TEXT "nip-text" #define STOCK_SMUDGE "nip-smudge" #define STOCK_FLOOD "nip-flood" #define STOCK_FLOOD_BLOB "nip-floodblob" #define STOCK_RECT "nip-rect" #define STOCK_MOVE "nip-move" #define STOCK_LOCK "nip-lock" #define STOCK_ALERT "nip-alert" #define STOCK_SELECT "nip-select" #define STOCK_LED_RED "nip-led-red" #define STOCK_LED_GREEN "nip-led-green" #define STOCK_LED_BLUE "nip-led-blue" #define STOCK_LED_YELLOW "nip-led-yellow" #define STOCK_LED_CYAN "nip-led-cyan" #define STOCK_LED_OFF "nip-led-off" /* How much we decompile for error messages. */ #define MAX_ERROR_FRAG (100) /* win32 adds '_', sometimes. */ #ifdef OS_WIN32 #ifndef popen #define popen(b,m) _popen(b,m) #endif /*popen*/ #ifndef pclose #define pclose(f) _pclose(f) #endif /*pclose*/ #define mktemp(f) _mktemp(f) #endif /*OS_WIN32*/ /* Fwd ref these. */ typedef struct _Watch Watch; typedef struct _Toolitem Toolitem; typedef struct _BuiltinInfo BuiltinInfo; typedef struct _Classmodel Classmodel; typedef struct _Colour Colour; typedef struct _Column Column; typedef struct _Columnview Columnview; typedef struct _Compile Compile; typedef struct _Conversion Conversion; typedef struct _Conversionview Conversionview; typedef struct _Expr Expr; typedef struct _Filemodel Filemodel; typedef struct _Heap Heap; typedef struct _HeapBlock HeapBlock; typedef struct _Heapmodel Heapmodel; typedef struct _iArrow iArrow; typedef struct _iImage iImage; typedef struct _Imagedisplay Imagedisplay; typedef struct _Managed Managed; typedef struct _Managedfile Managedfile; typedef struct _Managedgvalue Managedgvalue; typedef struct _Managedgobject Managedgobject; typedef struct _Managedstring Managedstring; typedef struct _Imageinfo Imageinfo; typedef struct _Imagepresent Imagepresent; typedef struct _Imagemodel Imagemodel; typedef struct _iRegion iRegion; typedef struct _iRegiongroup iRegiongroup; typedef struct _Link Link; typedef struct _LinkExpr LinkExpr; typedef struct _Model Model; typedef struct _iObject iObject; typedef struct _iContainer iContainer; typedef struct _Paintboxview Paintboxview; typedef struct _ParseConst ParseConst; typedef struct _ParseNode ParseNode; typedef struct _Program Program; typedef struct _String String; typedef struct _Number Number; typedef struct _Reduce Reduce; typedef struct _Regionview Regionview; typedef struct _Rhs Rhs; typedef struct _Rhsview Rhsview; typedef struct _Row Row; typedef struct _Rowview Rowview; typedef struct _Statusview Statusview; typedef struct _Plotstatus Plotstatus; typedef struct _Plot Plot; typedef struct _Plotwindow Plotwindow; typedef struct _Plotpresent Plotpresent; typedef struct _Plotmodel Plotmodel; typedef struct _Graphwindow Graphwindow; typedef struct _Subcolumn Subcolumn; typedef struct _Subcolumnview Subcolumnview; typedef struct _Symbol Symbol; typedef struct _Tool Tool; typedef struct _Toolkit Toolkit; typedef struct _Toolkitgroup Toolkitgroup; typedef struct _Toolkitgroupview Toolkitgroupview; typedef struct _Toolkitview Toolkitview; typedef struct _Toolview Toolview; typedef struct _Trace Trace; typedef struct _Preview Preview; typedef struct _Infobar Infobar; typedef struct _iError iError; typedef struct _Log Log; typedef struct _vObject vObject; typedef struct _View View; typedef struct _Workspace Workspace; typedef struct _Workspaceview Workspaceview; typedef struct _Workspaceroot Workspaceroot; typedef struct _Workspacegroup Workspacegroup; typedef struct _Workspacegroupview Workspacegroupview; typedef struct _Prefworkspaceview Prefworkspaceview; typedef struct _Prefcolumnview Prefcolumnview; typedef struct _iText iText; typedef struct _Expression Expression; typedef struct _Mainw Mainw; typedef struct _Toolviewitemgroup Toolviewitemgroup; typedef struct _Panechild Panechild; typedef struct _Toolkitbrowser Toolkitbrowser; typedef struct _Workspacedefs Workspacedefs; /* container map function typedefs. */ typedef void *(*row_map_fn)( Row *, void *, void *, void * ); typedef void *(*symbol_map_fn)( Symbol *, void *, void *, void * ); typedef void *(*column_map_fn)( Column *, void * ); typedef void *(*view_map_fn)( View *, void *, void * ); typedef void *(*rowview_map_fn)( Rowview *, void * ); typedef void *(*workspace_map_fn)( Workspace *, void * ); typedef void *(*toolkit_map_fn)( Toolkit *, void *, void * ); typedef void *(*tool_map_fn)( Tool *, void *, void * ); /* Util stuff. */ #include "util.h" #include "gtkutil.h" #include "path.h" #include "iobject.h" #include "icontainer.h" #include "iwindow.h" #include "idialog.h" #include "boxes.h" #include "popupbutton.h" #include "imageheader.h" #include "filesel.h" #include "managed.h" #include "managedfile.h" #include "managedgvalue.h" #include "managedgobject.h" #include "imageinfo.h" #include "imagedisplay.h" #include "colourdisplay.h" #include "imagemodel.h" #include "imagepresent.h" #include "floatwindow.h" #include "imageview.h" #include "tslider.h" #include "pane.h" #include "progress.h" /* Basic ip includes (order important). */ #include "tree.h" #include "heap.h" #include "managedstring.h" #include "class.h" #include "link.h" #include "expr.h" #include "model.h" #include "paintboxview.h" #include "conversion.h" #include "heapmodel.h" #include "classmodel.h" #include "filemodel.h" #include "symbol.h" #include "workspace.h" #include "workspaceroot.h" #include "workspacegroup.h" #include "toolkitgroup.h" #include "secret.h" #include "action.h" #include "reduce.h" #include "vobject.h" #include "vipsobject.h" #include "view.h" #include "graphicview.h" #include "spin.h" #include "row.h" #include "rowview.h" #include "subcolumn.h" #include "subcolumnview.h" #include "rhs.h" #include "rhsview.h" #include "workspaceview.h" #include "workspacegroupview.h" #include "toolkitgroupview.h" #include "column.h" #include "columnview.h" #include "toolkit.h" #include "tool.h" #include "toolkitview.h" #include "toolview.h" #include "watch.h" #include "value.h" #include "panechild.h" /* Per module includes, any order */ #include "workspacedefs.h" #include "toolkitbrowser.h" #include "defbrowser.h" #include "log.h" #include "error.h" #include "trace.h" #include "program.h" #include "conversionview.h" #include "statusview.h" #include "plotstatus.h" #include "mainw.h" #include "preview.h" #include "builtin.h" #include "compile.h" #include "dump.h" #include "main.h" #include "predicate.h" #include "slider.h" #include "clock.h" #include "pathname.h" #include "fontname.h" #include "group.h" #include "real.h" #include "vector.h" #include "colour.h" #include "number.h" #include "istring.h" #include "editview.h" #include "expression.h" #include "expressionview.h" #include "stringview.h" #include "numberview.h" #include "matrix.h" #include "matrixview.h" #include "plot.h" #ifdef HAVE_LIBGOFFICE #include "plotview.h" #endif /*HAVE_LIBGOFFICE*/ #include "plotmodel.h" #include "plotpresent.h" #include "plotwindow.h" #include "graphwindow.h" #include "option.h" #include "optionview.h" #include "iimage.h" #include "iregion.h" #include "iregiongroup.h" #include "iarrow.h" #include "valueview.h" #include "sliderview.h" #include "pathnameview.h" #include "fontnameview.h" #include "colourview.h" #include "iimageview.h" #include "iregionview.h" #include "iregiongroupview.h" #include "prefs.h" #include "prefworkspaceview.h" #include "prefcolumnview.h" #include "regionview.h" #include "itext.h" #include "itextview.h" #include "toggle.h" #include "toggleview.h" #include "call.h" #include "cache.h" #include "parser.h" #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ #endif /*IP_H*/ ================================================ FILE: src/iregion.c ================================================ /* an ip region class object in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static iImageClass *parent_class = NULL; void iregion_instance_destroy( iRegionInstance *instance ) { instance->image_class.type = ELEMENT_NOVAL; instance->image_class.ele = (void *) 8; MANAGED_UNREF( instance->ii ); instance->classmodel = NULL; instance->iregiongroup = NULL; heap_unregister_element( reduce_context->heap, &instance->image_class ); } void iregion_instance_init( iRegionInstance *instance, Classmodel *classmodel ) { instance->image_class.type = ELEMENT_NOVAL; instance->image_class.ele = (void *) 9; instance->ii = NULL; instance->area.left = 0; instance->area.top = 0; instance->area.width = 0; instance->area.height = 0; instance->classmodel = classmodel; instance->iregiongroup = NULL; heap_register_element( reduce_context->heap, &instance->image_class ); } gboolean iregion_instance_update( iRegionInstance *instance, PElement *root ) { PElement image; PElement image_class; Imageinfo *value; int left, top, width, height; if( !class_get_member_class( root, MEMBER_IMAGE, "Image", &image ) || !class_get_member_image( &image, MEMBER_VALUE, &value ) || !class_get_member_int( root, MEMBER_LEFT, &left ) || !class_get_member_int( root, MEMBER_TOP, &top ) || !class_get_member_int( root, MEMBER_WIDTH, &width ) || !class_get_member_int( root, MEMBER_HEIGHT, &height ) ) return( FALSE ); instance->area.left = left; instance->area.top = top; instance->area.width = width; instance->area.height = height; MANAGED_UNREF( instance->ii ); instance->ii = value; MANAGED_REF( value ); PEPOINTE( &image_class, &instance->image_class ); PEPUTPE( &image_class, &image ); return( TRUE ); } static void iregion_finalize( GObject *gobject ) { iRegion *iregion; #ifdef DEBUG printf( "iregion_finalize\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_IREGION( gobject ) ); iregion = IREGION( gobject ); /* My instance finalize stuff. */ iregion_instance_destroy( &iregion->instance ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void * iregion_generate_caption_sub( iImage *iimage, iRegion *iregion, gboolean *first ) { iImage *our_iimage = IIMAGE( iregion ); Workspace *ws = HEAPMODEL( iregion )->row->ws; Row *row = HEAPMODEL( iimage )->row; /* Supress this name in the caption if it's a superclass. If this * thing is on a super, it's on the subclass too ... not helpful to * have it twice. */ if( row->sym && !is_super( row->sym ) ) { if( *first ) *first = FALSE; else vips_buf_appends( &our_iimage->caption_buffer, ", " ); row_qualified_name_relative( ws->sym, row, &our_iimage->caption_buffer ); } return( NULL ); } static const char * iregion_generate_caption( iObject *iobject ) { iRegion *iregion = IREGION( iobject ); iImage *iimage = IIMAGE( iregion ); const int nimages = g_slist_length( CLASSMODEL( iregion )->iimages ); VipsBuf *buf = &iimage->caption_buffer; gboolean first; vips_buf_rewind( buf ); heapmodel_name( HEAPMODEL( iregion ), buf ); vips_buf_appendf( buf, " " ); /* Expands to (eg.) "Region on A1 at (10, 10), size (50, 50)" */ vips_buf_appendf( buf, _( "on" ) ); vips_buf_appendf( buf, " " ); if( nimages > 1 ) vips_buf_appendf( buf, "[" ); first = TRUE; slist_map2( CLASSMODEL( iregion )->iimages, (SListMap2Fn) iregion_generate_caption_sub, iregion, &first ); if( nimages > 1 ) vips_buf_appendf( buf, "]" ); vips_buf_appendf( buf, " " ); vips_buf_appendf( buf, _( "at (%d, %d), size (%d, %d)" ), iregion->instance.area.left, iregion->instance.area.top, iregion->instance.area.width, iregion->instance.area.height ); return( vips_buf_all( buf ) ); } static void iregion_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Classmodel *classmodel = CLASSMODEL( client ); iRegionInstance *instance = classmodel_get_instance( classmodel ); Stringset *ss = STRINGSET( iwnd ); Rect area; StringsetChild *left = stringset_child_get( ss, _( "Left" ) ); StringsetChild *top = stringset_child_get( ss, _( "Top" ) ); StringsetChild *width = stringset_child_get( ss, _( "Width" ) ); StringsetChild *height = stringset_child_get( ss, _( "Height" ) ); if( !get_geditable_int( left->entry, &area.left ) || !get_geditable_int( top->entry, &area.top ) || !get_geditable_int( width->entry, &area.width ) || !get_geditable_int( height->entry, &area.height ) ) { nfn( sys, IWINDOW_ERROR ); return; } if( instance ) { instance->area = area; classmodel_update( classmodel ); symbol_recalculate_all(); } nfn( sys, IWINDOW_YES ); } static View * iregion_view_new( Model *model, View *parent ) { return( iregionview_new() ); } /* Pop up a iregion edit box. Shared with iarrow.c. */ void iregion_edit( GtkWidget *parent, Model *model ) { Classmodel *classmodel = CLASSMODEL( model ); iRegionInstance *instance = classmodel_get_instance( classmodel ); GtkWidget *ss = stringset_new(); if( instance ) { char txt[256]; im_snprintf( txt, 256, "%d", instance->area.left ); stringset_child_new( STRINGSET( ss ), _( "Left" ), txt, _( "Left edge of region" ) ); im_snprintf( txt, 256, "%d", instance->area.top ); stringset_child_new( STRINGSET( ss ), _( "Top" ), txt, _( "Top edge of region" ) ); im_snprintf( txt, 256, "%d", instance->area.width ); stringset_child_new( STRINGSET( ss ), _( "Width" ), txt, _( "Width of region" ) ); im_snprintf( txt, 256, "%d", instance->area.height ); stringset_child_new( STRINGSET( ss ), _( "Height" ), txt, _( "Height of region" ) ); } iwindow_set_title( IWINDOW( ss ), _( "Edit %s %s" ), IOBJECT_GET_CLASS_NAME( model ), IOBJECT( HEAPMODEL( model )->row )->name ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, classmodel ); idialog_add_ok( IDIALOG( ss ), iregion_done_cb, _( "Set %s" ), IOBJECT_GET_CLASS_NAME( model ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( parent ) ); idialog_set_iobject( IDIALOG( ss ), IOBJECT( model ) ); idialog_set_pinup( IDIALOG( ss ), TRUE ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } /* Shared with iarrow.c. */ void iregion_parent_add( iContainer *child ) { ICONTAINER_CLASS( parent_class )->parent_add( child ); /* Now we're all linked up, make a child model to handle client * displays on imageviews. */ (void) iregiongroup_new( CLASSMODEL( child ) ); } /* Shared with iarrow.c. */ xmlNode * iregion_save( Model *model, xmlNode *xnode ) { /* Get our parent class. We can't just use the global parent_class, * since due to our lame MI scheme, this method may be called for * iarrow/ipoint etc. as well as iregion ... look up dynamically. */ gpointer parent_class = PARENT_CLASS_DYNAMIC( model ); iRegionInstance *instance = classmodel_get_instance( CLASSMODEL( model ) ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( instance && CLASSMODEL( model )->edited ) { Rect *area = &instance->area; if( !set_iprop( xthis, "left", area->left ) || !set_iprop( xthis, "top", area->top ) || !set_iprop( xthis, "width", area->width ) || !set_iprop( xthis, "height", area->height ) ) return( NULL ); } return( xthis ); } /* Shared with iarrow.c. */ gboolean iregion_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { gpointer parent_class = PARENT_CLASS_DYNAMIC( model ); iRegionInstance *instance = classmodel_get_instance( CLASSMODEL( model ) ); g_assert( IS_RHS( parent ) ); if( instance ) { Rect *area = &instance->area; if( get_iprop( xnode, "left", &area->left ) && get_iprop( xnode, "top", &area->top ) && get_iprop( xnode, "width", &area->width ) && get_iprop( xnode, "height", &area->height ) ) classmodel_set_edited( CLASSMODEL( model ), TRUE ); } return( MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ); } /* Need to implement _update_heap(), as not all model fields are directly * editable ... some are set only from expr. See also iimage.c. Shared with * iarrow.c. */ void * iregion_update_heap( Heapmodel *heapmodel ) { gpointer parent_class = PARENT_CLASS_DYNAMIC( heapmodel ); iRegionInstance *instance = classmodel_get_instance( CLASSMODEL( heapmodel ) ); Expr *expr = heapmodel->row->expr; Rect area; PElement pe; if( instance ) { /* Save any model fields that may have been set by _load() and * which might be zapped by _get_instance(). */ area = instance->area; /* Look for the base instance and update from that. */ if( !class_get_exact( &expr->root, IOBJECT( heapmodel )->name, &pe ) ) return( FALSE ); if( !iregion_instance_update( instance, &pe ) ) return( heapmodel ); /* Restore model fields from _load(). */ instance->area = area; } /* Classmodel _update_heap() will do _instance_new() from the fixed up * model. */ return( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) ); } static void * iregion_update_model( Heapmodel *heapmodel ) { iRegion *iregion = IREGION( heapmodel ); if( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ) return( heapmodel ); /* Update who-has-displays-on-what stuff. */ classmodel_iimage_update( CLASSMODEL( iregion ), iregion->instance.ii ); /* Make sure the caption is regenerated. */ iobject_changed( IOBJECT( heapmodel ) ); return( NULL ); } /* Update iRegion from heap. Shared with iarrow.c. */ gboolean iregion_class_get( Classmodel *classmodel, PElement *root ) { gpointer parent_class = PARENT_CLASS_DYNAMIC( classmodel ); iRegionInstance *instance = classmodel_get_instance( classmodel ); #ifdef DEBUG printf( "iregion_class_get: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ if( instance && !iregion_instance_update( instance, root ) ) return( FALSE ); return( CLASSMODEL_CLASS( parent_class )->class_get( classmodel, root ) ); } /* Make a new "fn value" application. Shared with iarrow.c. */ gboolean iregion_class_new( Classmodel *classmodel, PElement *fn, PElement *out ) { Heap *heap = reduce_context->heap; iRegionInstance *instance = classmodel_get_instance( classmodel ); PElement rhs; #ifdef DEBUG printf( "iregion_class_new\n" ); #endif /*DEBUG*/ /* Make application nodes. */ if( instance ) { heap_appl_init( out, fn ); if( !heap_appl_add( heap, out, &rhs ) || !heap_element_new( heap, &instance->image_class, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_real_new( heap, instance->area.left, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_real_new( heap, instance->area.top, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_real_new( heap, instance->area.width, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_real_new( heap, instance->area.height, &rhs ) ) return( FALSE ); } return( TRUE ); } static void * iregion_get_instance( Classmodel *classmodel ) { iRegion *iregion = IREGION( classmodel ); return( &iregion->instance ); } static void iregion_class_init( iRegionClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = iregion_finalize; iobject_class->user_name = _( "Region" ); iobject_class->generate_caption = iregion_generate_caption; icontainer_class->parent_add = iregion_parent_add; model_class->view_new = iregion_view_new; model_class->edit = iregion_edit; model_class->save = iregion_save; model_class->load = iregion_load; heapmodel_class->update_heap = iregion_update_heap; heapmodel_class->update_model = iregion_update_model; classmodel_class->class_get = iregion_class_get; classmodel_class->class_new = iregion_class_new; classmodel_class->get_instance = iregion_get_instance; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void iregion_init( iRegion *iregion ) { iregion_instance_init( &iregion->instance, CLASSMODEL( iregion ) ); iobject_set( IOBJECT( iregion ), CLASS_REGION, NULL ); } GType iregion_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( iRegionClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) iregion_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( iRegion ), 32, /* n_preallocs */ (GInstanceInitFunc) iregion_init, }; type = g_type_register_static( TYPE_IIMAGE, "iRegion", &info, 0 ); } return( type ); } ================================================ FILE: src/iregion.h ================================================ /* a ip region class in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IREGION (iregion_get_type()) #define IREGION( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IREGION, iRegion )) #define IREGION_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_IREGION, iRegionClass)) #define IS_IREGION( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IREGION )) #define IS_IREGION_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IREGION )) #define IREGION_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_IREGION, iRegionClass )) /* Handy for indexing arrays. */ typedef enum iRegionType { IREGION_MARK = 0, IREGION_HGUIDE, IREGION_VGUIDE, IREGION_ARROW, IREGION_REGION, IREGION_AREA } iRegionType; /* Our instance vars ... packaged up for code sharing. */ typedef struct { /* Stuff from the heap. */ Element image_class; /* Child image class */ Imageinfo *ii; Rect area; /* Client display stuff. */ Classmodel *classmodel; iRegiongroup *iregiongroup; } iRegionInstance; struct _iRegion { iImage parent_class; /* Class fields shared with iarrow.c. */ iRegionInstance instance; }; typedef struct _iRegionClass { iImageClass parent_class; /* My methods. */ } iRegionClass; void iregion_instance_destroy( iRegionInstance *instance ); void iregion_instance_init( iRegionInstance *instance, Classmodel *classmodel ); gboolean iregion_instance_update( iRegionInstance *instance, PElement *root ); void iregion_edit( GtkWidget *parent, Model *model ); void iregion_parent_add( iContainer *child ); xmlNode *iregion_save( Model *model, xmlNode *xnode ); gboolean iregion_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ); void *iregion_update_heap( Heapmodel *heapmodel ); gboolean iregion_class_get( Classmodel *classmodel, PElement *root ); gboolean iregion_class_new( Classmodel *classmodel, PElement *fn, PElement *out ); GType iregion_get_type( void ); ================================================ FILE: src/iregiongroup.c ================================================ /* base model for a client regions on an imageview */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void * iregiongroup_update_model( Heapmodel *heapmodel ) { #ifdef DEBUG printf( "iregiongroup_update_model: " ); row_name_print( heapmodel->row ); printf( "\n" ); #endif /*DEBUG*/ if( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ) return( heapmodel ); /* Only display most-derived classes. Don't display "this". */ if( heapmodel->row->sym ) model_display( MODEL( heapmodel ), !is_super( heapmodel->row->sym ) && !is_this( heapmodel->row->sym ) ); return( NULL ); } static View * iregiongroup_view_new( Model *model, View *parent ) { return( iregiongroupview_new() ); } static void iregiongroup_class_init( iRegiongroupClass *class ) { ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ heapmodel_class->update_model = iregiongroup_update_model; model_class->view_new = iregiongroup_view_new; } static void iregiongroup_init( iRegiongroup *iregiongroup ) { /* Display turned on in _update_model() above. */ MODEL( iregiongroup )->display = FALSE; } GType iregiongroup_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( iRegiongroupClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) iregiongroup_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( iRegiongroup ), 32, /* n_preallocs */ (GInstanceInitFunc) iregiongroup_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "iRegiongroup", &info, 0 ); } return( type ); } iRegiongroup * iregiongroup_new( Classmodel *classmodel ) { iRegiongroup *iregiongroup; iregiongroup = IREGIONGROUP( g_object_new( TYPE_IREGIONGROUP, NULL ) ); icontainer_child_add( ICONTAINER( classmodel ), ICONTAINER( iregiongroup ), -1 ); #ifdef DEBUG printf( "iregiongroup_new: " ); row_name_print( HEAPMODEL( classmodel )->row ); printf( "\n" ); #endif /*DEBUG*/ return( iregiongroup ); } ================================================ FILE: src/iregiongroup.h ================================================ /* base model for a client regions on an imageview */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IREGIONGROUP (iregiongroup_get_type()) #define IREGIONGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_IREGIONGROUP, iRegiongroup )) #define IREGIONGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_IREGIONGROUP, iRegiongroupClass)) #define IS_IREGIONGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_IREGIONGROUP )) #define IS_IREGIONGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_IREGIONGROUP )) #define IREGIONGROUP_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_IREGIONGROUP, iRegiongroupClass )) struct _iRegiongroup { Classmodel parent_class; }; typedef struct _iRegiongroupClass { ClassmodelClass parent_class; /* My methods. */ } iRegiongroupClass; GType iregiongroup_get_type( void ); iRegiongroup *iregiongroup_new( Classmodel *classmodel ); ================================================ FILE: src/iregiongroupview.c ================================================ /* coordinate the display of regionviews on imageviews */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static iRegiongroup * iregiongroupview_get_iregiongroup( iRegiongroupview *iregiongroupview ) { return( IREGIONGROUP( VOBJECT( iregiongroupview )->iobject ) ); } static Classmodel * iregiongroupview_get_classmodel( iRegiongroupview *iregiongroupview ) { iRegiongroup *iregiongroup; if( (iregiongroup = iregiongroupview_get_iregiongroup( iregiongroupview )) ) return( CLASSMODEL( ICONTAINER( iregiongroup )->parent ) ); return( NULL ); } static void iregiongroupview_destroy( GtkObject *object ) { iRegiongroupview *iregiongroupview; #ifdef DEBUG printf( "iregiongroupview_destroy: %p\n", object ); #endif /*DEBUG*/ g_return_if_fail( object != NULL ); g_return_if_fail( IS_IREGIONGROUPVIEW( object ) ); iregiongroupview = IREGIONGROUPVIEW( object ); /* Destroy all regionviews we manage. */ slist_map( iregiongroupview->classmodel->views, (SListMapFn) object_destroy, NULL ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* What we track during a refresh. */ typedef struct { GSList *notused; iRegiongroupview *iregiongroupview; Classmodel *classmodel; iImage *iimage; Imagepresent *ip; } iRegiongroupviewRefreshState; static Regionview * iregiongroupview_refresh_imageview_test( Regionview *regionview, iRegiongroupviewRefreshState *irs ) { if( regionview->classmodel == irs->classmodel && regionview->ip == irs->ip ) return( regionview ); return( NULL ); } static void * iregiongroupview_refresh_imageview( Imagepresent *ip, iRegiongroupviewRefreshState *irs ) { Regionview *regionview; irs->ip = ip; /* Do we have a Regionview for this iv already? */ if( (regionview = slist_map( irs->notused, (SListMapFn) iregiongroupview_refresh_imageview_test, irs )) ) { /* Yes ... reuse. */ irs->notused = g_slist_remove( irs->notused, regionview ); } else { /* Nope ... make a new one. */ iRegionInstance *instance = classmodel_get_instance( irs->classmodel ); PElement *root = &HEAPMODEL( irs->classmodel )->row->expr->root; if( instance ) { Regionview *regionview = regionview_new( irs->classmodel, &instance->area, ip ); #ifdef DEBUG printf( "iregiongroupview_refresh_imageview: " "creating new regionview\n" ); #endif /*DEBUG*/ /* Set the display type from the heap class name. */ regionview_set_type( regionview, root ); } } return( NULL ); } static void * iregiongroupview_refresh_iimage( iImage *iimage, iRegiongroupviewRefreshState *irs ) { irs->iimage = iimage; slist_map( iimage->views, (SListMapFn) iregiongroupview_refresh_imageview, irs ); return( NULL ); } static void iregiongroupview_refresh( vObject *vobject ) { iRegiongroupview *iregiongroupview = IREGIONGROUPVIEW( vobject ); iRegiongroupviewRefreshState irs; #ifdef DEBUG printf( "iregiongroupview_refresh\n" ); printf( "watching model %s %s\n", G_OBJECT_TYPE_NAME( vobject->iobject ), NN( IOBJECT( vobject->iobject )->name ) ); #endif /*DEBUG*/ iregiongroupview->classmodel = iregiongroupview_get_classmodel( iregiongroupview ); if( iregiongroupview->classmodel ) { /* Make a note of all the displays we have now, loop over the * displays we should have, reusing when possible ... remove * any unused displays at the end. */ irs.classmodel = iregiongroupview->classmodel; irs.notused = g_slist_copy( irs.classmodel->views ); irs.iregiongroupview = iregiongroupview; slist_map( irs.classmodel->iimages, (SListMapFn) iregiongroupview_refresh_iimage, &irs ); /* Remove all the regionviews we've not used. */ slist_map( irs.notused, (SListMapFn) object_destroy, NULL ); IM_FREEF( g_slist_free, irs.notused ); } VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void iregiongroupview_class_init( iRegiongroupviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = iregiongroupview_destroy; /* Create signals. */ /* Init methods. */ vobject_class->refresh = iregiongroupview_refresh; } static void iregiongroupview_init( iRegiongroupview *iregiongroupview ) { #ifdef DEBUG printf( "iregiongroupview_init\n" ); #endif /*DEBUG*/ } GtkType iregiongroupview_get_type( void ) { static GtkType iregiongroupview_type = 0; if( !iregiongroupview_type ) { static const GtkTypeInfo info = { "iRegiongroupview", sizeof( iRegiongroupview ), sizeof( iRegiongroupviewClass ), (GtkClassInitFunc) iregiongroupview_class_init, (GtkObjectInitFunc) iregiongroupview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; iregiongroupview_type = gtk_type_unique( TYPE_VIEW, &info ); } return( iregiongroupview_type ); } View * iregiongroupview_new( void ) { iRegiongroupview *iregiongroupview = gtk_type_new( TYPE_IREGIONGROUPVIEW ); #ifdef DEBUG printf( "iregiongroupview_new\n" ); #endif /*DEBUG*/ return( VIEW( iregiongroupview ) ); } ================================================ FILE: src/iregiongroupview.h ================================================ /* coordinate the display of regionviews on imageviews */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IREGIONGROUPVIEW (iregiongroupview_get_type()) #define IREGIONGROUPVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_IREGIONGROUPVIEW, iRegiongroupview )) #define IREGIONGROUPVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IREGIONGROUPVIEW, \ iRegiongroupviewClass )) #define IS_IREGIONGROUPVIEW( obj ) \ (GTK_CHECK_TYPE( (obj), TYPE_IREGIONGROUPVIEW )) #define IS_IREGIONGROUPVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IREGIONGROUPVIEW )) typedef struct _iRegiongroupview { View parent_class; /* Keep our classmodel here, we need it during destroy. */ Classmodel *classmodel; } iRegiongroupview; typedef struct _iRegiongroupviewClass { ViewClass parent_class; /* My methods. */ } iRegiongroupviewClass; GtkType iregiongroupview_get_type( void ); View *iregiongroupview_new( void ); ================================================ FILE: src/iregionview.c ================================================ /* display a region in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static iImageviewClass *parent_class = NULL; static void iregionview_class_init( iRegionviewClass *class ) { parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ } static void iregionview_init( iRegionview *iregionview ) { #ifdef DEBUG printf( "iregionview_init\n" ); #endif /*DEBUG*/ } GtkType iregionview_get_type( void ) { static GtkType iregionview_type = 0; if( !iregionview_type ) { static const GtkTypeInfo info = { "iRegionview", sizeof( iRegionview ), sizeof( iRegionviewClass ), (GtkClassInitFunc) iregionview_class_init, (GtkObjectInitFunc) iregionview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; iregionview_type = gtk_type_unique( TYPE_IIMAGEVIEW, &info ); } return( iregionview_type ); } View * iregionview_new( void ) { iRegionview *iregionview = gtk_type_new( TYPE_IREGIONVIEW ); #ifdef DEBUG printf( "iregionview_new\n" ); #endif /*DEBUG*/ return( VIEW( iregionview ) ); } ================================================ FILE: src/iregionview.h ================================================ /* display a region in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_IREGIONVIEW (iregionview_get_type()) #define IREGIONVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_IREGIONVIEW, iRegionview )) #define IREGIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IREGIONVIEW, iRegionviewClass )) #define IS_IREGIONVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_IREGIONVIEW )) #define IS_IREGIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IREGIONVIEW )) typedef struct _iRegionview { iImageview parent_class; } iRegionview; typedef struct _iRegionviewClass { iImageviewClass parent_class; /* My methods. */ } iRegionviewClass; GtkType iregionview_get_type( void ); View *iregionview_new( void ); ================================================ FILE: src/istring.h ================================================ /* an editable string in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_STRING (string_get_type()) #define STRING( obj ) (GTK_CHECK_CAST( (obj), TYPE_STRING, String )) #define STRING_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_STRING, StringClass )) #define IS_STRING( obj ) (GTK_CHECK_TYPE( (obj), TYPE_STRING )) #define IS_STRING_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_STRING )) struct _String { Classmodel parent_class; /* Class fields. */ char *value; }; typedef struct _StringClass { ClassmodelClass parent_class; /* My methods. */ } StringClass; GType string_get_type( void ); ================================================ FILE: src/itext.c ================================================ /* a text item in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static HeapmodelClass *parent_class = NULL; static void itext_finalize( GObject *gobject ) { iText *itext; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_ITEXT( gobject ) ); itext = ITEXT( gobject ); #ifdef DEBUG printf( "itext_destroy\n" ); #endif /*DEBUG*/ /* My instance destroy stuff. */ IM_FREE( itext->formula ); IM_FREE( itext->formula_default ); vips_buf_destroy( &itext->value ); vips_buf_destroy( &itext->decompile ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void itext_info( iObject *iobject, VipsBuf *buf ) { iText *itext = ITEXT( iobject ); vips_buf_appends( buf, _( "Formula" ) ); vips_buf_appendf( buf, ": %s\n", NN( itext->formula ) ); } /* Fwd ref this. */ static gboolean itext_add_element( VipsBuf *buf, PElement *base, gboolean top, gboolean bracket ); /* Sub-fn of below, callback for list print. Eval and print the item into * the buffer, separating with commas as required. */ static void * itext_add_list( PElement *base, VipsBuf *buf, gboolean *first ) { Reduce *rc = reduce_context; if( *first ) *first = FALSE; else vips_buf_appends( buf, ", " ); /* Reduce the head, and print. */ if( !reduce_pelement( rc, reduce_spine, base ) ) return( base ); if( !itext_add_element( buf, base, FALSE, FALSE ) ) return( base ); /* Buffer full? Abort list print. */ if( buf->full ) return( base ); return( NULL ); } /* Sub-fn of below, callback for string print. Print the chars into the * buffer. */ static void * itext_add_string( PElement *base, VipsBuf *buf ) { Reduce *rc = reduce_context; /* Reduce the head, and add the char. */ if( !reduce_pelement( rc, reduce_spine, base ) ) return( base ); if( PEISCHAR( base ) ) /* Don't escape chars in string mode. */ vips_buf_appendf( buf, "%c", PEGETCHAR( base ) ); else { /* Help! Fall back to ordinary item print. */ vips_buf_appends( buf, ", " ); if( !itext_add_element( buf, base, FALSE, FALSE ) ) return( base ); } /* Buffer full? Abort string print. */ if( buf->full ) return( base ); return( NULL ); } /* Print a char ... we need to escape \n etc. */ static void itext_add_char( int ch, VipsBuf *buf ) { char in[2]; char out[3]; in[0] = ch; in[1] = '\0'; my_strecpy( out, in, FALSE ); vips_buf_appends( buf, out ); } /* Print a complex. */ static void itext_add_complex( double rp, double ip, VipsBuf *buf ) { if( PRINT_CARTESIAN ) vips_buf_appendf( buf, "(%.12g, %.12g)", rp, ip ); else { if( rp == 0 ) { if( ip == 0 ) vips_buf_appendf( buf, "0" ); else vips_buf_appendf( buf, "%.12gj", ip ); } else if( ip == 0 ) vips_buf_appendf( buf, "%.12g", rp ); else vips_buf_appendf( buf, "%.12g + %.12gj", rp, ip ); } } /* Try to decompile. */ static gboolean itext_decompile_element( VipsBuf *buf, PElement *base, gboolean top ) { Reduce *rc = reduce_context; gboolean result; /* Set the value label for a tally entry. */ if( PEISNOVAL( base ) ) vips_buf_appends( buf, _( "no value" ) ); else if( PEISREAL( base ) ) vips_buf_appendf( buf, "%g", PEGETREAL( base ) ); else if( PEISBOOL( base ) ) vips_buf_appends( buf, bool_to_char( PEGETBOOL( base ) ) ); else if( PEISCHAR( base ) ) { vips_buf_appends( buf, "'" ); itext_add_char( (int) PEGETCHAR( base ), buf ); vips_buf_appends( buf, "'" ); } else if( PEISCOMPLEX( base ) ) itext_add_complex( PEGETREALPART( base ), PEGETIMAGPART( base ), buf ); else if( PEISMANAGEDSTRING( base ) ) { Managedstring *managedstring = PEGETMANAGEDSTRING( base ); vips_buf_appendf( buf, "\"%s\"", managedstring->string ); } else if( PEISELIST( base ) ) { vips_buf_appends( buf, "[ ]" ); } else if( !heap_is_string( base, &result ) ) /* Eval error. */ return( FALSE ); else if( result ) { vips_buf_appends( buf, "\"" ); if( heap_map_list( base, (heap_map_list_fn) itext_add_string, buf, NULL ) ) return( FALSE ); vips_buf_appends( buf, "\"" ); } else if( PEISLIST( base ) ) { gboolean first = TRUE; vips_buf_appends( buf, "[" ); if( heap_map_list( base, (heap_map_list_fn) itext_add_list, buf, &first ) ) return( FALSE ); vips_buf_appends( buf, "]" ); } else if( PEISIMAGE( base ) ) { Imageinfo *ii = PEGETII( base ); if( !top ) vips_buf_appends( buf, "(" ); if( ii && IOBJECT( ii )->name ) vips_buf_appendf( buf, "vips_image \"%s\"", IOBJECT( ii )->name ); else vips_buf_appendf( buf, "vips_image " ); if( !top ) vips_buf_appends( buf, ")" ); } else if( PEISMANAGED( base ) ) { Managed *managed; if( !(managed = PEGETMANAGED( base )) ) vips_buf_appendf( buf, "" ); else { vips_buf_appendf( buf, "<%s ", G_OBJECT_TYPE_NAME( managed ) ); iobject_info( IOBJECT( managed ), buf ); vips_buf_appends( buf, ">" ); } } else if( PEISCLASS( base ) ) { Compile *compile = PEGETCLASSCOMPILE( base ); PElement params; int i; if( !top ) vips_buf_appends( buf, "(" ); symbol_qualified_name( compile->sym, buf ); /* Skip over the secrets, then decompile all the args. */ PEGETCLASSSECRET( ¶ms, base ); for( i = 0; i < compile->nsecret; i++ ) { HeapNode *hn = PEGETVAL( ¶ms ); PEPOINTRIGHT( hn, ¶ms ); } for( i = 0; i < compile->nparam; i++ ) { HeapNode *hn = PEGETVAL( ¶ms ); HeapNode *sv = GETLEFT( hn ); PElement value; PEPOINTRIGHT( sv, &value ); vips_buf_appends( buf, " " ); if( !itext_decompile_element( buf, &value, FALSE ) ) return( FALSE ); PEPOINTRIGHT( hn, ¶ms ); } if( !top ) vips_buf_appends( buf, ")" ); } else if( PEISSYMREF( base ) ) vips_buf_appends( buf, IOBJECT( PEGETSYMREF( base ) )->name ); else if( PEISTAG( base ) ) vips_buf_appends( buf, PEGETTAG( base ) ); else graph_pelement( rc->heap, buf, base, TRACE_FUNCTIONS ); return( TRUE ); } /* Little wrapper ... used for formatting error messages, etc. FALSE for eval * error. */ static gboolean itext_decompile( Reduce *rc, VipsBuf *buf, PElement *root ) { /* Evaluate and print off values. */ if( !reduce_pelement( rc, reduce_spine, root ) ) return( FALSE ); if( !itext_decompile_element( buf, root, TRUE ) && !buf->full ) /* Tally eval failed, and buffer is not full ... must * have been an eval error. */ return( FALSE ); return( TRUE ); } /* Print function for computed values. top is TRUE only for the very top level * output. bracket means we should bracket compound expressions. */ static gboolean itext_add_element( VipsBuf *buf, PElement *base, gboolean top, gboolean bracket ) { gboolean result; /* Set the value label for a tally entry. */ if( PEISNOVAL( base ) ) vips_buf_appends( buf, _( "no value" ) ); else if( PEISREAL( base ) ) vips_buf_appendf( buf, "%.7g", PEGETREAL( base ) ); else if( PEISBOOL( base ) ) vips_buf_appends( buf, bool_to_char( PEGETBOOL( base ) ) ); else if( PEISCHAR( base ) ) { vips_buf_appends( buf, "'" ); itext_add_char( (int) PEGETCHAR( base ), buf ); vips_buf_appends( buf, "'" ); } else if( PEISCOMPLEX( base ) ) { itext_add_complex( PEGETREALPART( base ), PEGETIMAGPART( base ), buf ); } else if( PEISMANAGEDSTRING( base ) ) { Managedstring *managedstring = PEGETMANAGEDSTRING( base ); if( !top ) vips_buf_appends( buf, "\"" ); vips_buf_appends( buf, managedstring->string ); if( !top ) vips_buf_appends( buf, "\"" ); } else if( PEISELIST( base ) ) { vips_buf_appends( buf, "[ ]" ); } else if( !heap_is_string( base, &result ) ) /* Eval error. */ return( FALSE ); else if( result ) { /* Only generate quotes for non-top-level string objects. */ if( !top ) vips_buf_appends( buf, "\"" ); /* Print string contents. */ if( heap_map_list( base, (heap_map_list_fn) itext_add_string, buf, NULL ) ) return( FALSE ); if( !top ) vips_buf_appends( buf, "\"" ); } else if( PEISLIST( base ) ) { gboolean first = TRUE; vips_buf_appends( buf, "[" ); if( heap_map_list( base, (heap_map_list_fn) itext_add_list, buf, &first ) ) return( FALSE ); vips_buf_appends( buf, "]" ); } else if( PEISIMAGE( base ) ) { vips_buf_appendf( buf, "<" ); vips_buf_appendi( buf, imageinfo_get( FALSE, PEGETII( base ) ) ); vips_buf_appendf( buf, ">" ); } else if( PEISMANAGED( base ) ) { Managed *managed = PEGETMANAGED( base ); vips_buf_appends( buf, "<" ); iobject_info( IOBJECT( managed ), buf ); vips_buf_appends( buf, ">" ); } else if( PEISCLASS( base ) ) { Compile *compile = PEGETCLASSCOMPILE( base ); PElement params; int i; if( bracket && compile->nparam ) vips_buf_appends( buf, "(" ); /* Name. */ symbol_qualified_name( compile->sym, buf ); /* Skip over the secrets, then value-ize all the args. */ PEGETCLASSSECRET( ¶ms, base ); for( i = 0; i < compile->nsecret; i++ ) { HeapNode *hn = PEGETVAL( ¶ms ); PEPOINTRIGHT( hn, ¶ms ); } for( i = 0; i < compile->nparam; i++ ) { HeapNode *hn = PEGETVAL( ¶ms ); HeapNode *sv = GETLEFT( hn ); PElement value; PEPOINTRIGHT( sv, &value ); vips_buf_appends( buf, " " ); if( !itext_add_element( buf, &value, FALSE, TRUE ) ) return( FALSE ); PEPOINTRIGHT( hn, ¶ms ); } if( bracket && compile->nparam ) vips_buf_appends( buf, ")" ); } else if( PEISSYMREF( base ) ) { Symbol *sym = PEGETSYMREF( base ); if( is_scope( sym ) ) { vips_buf_appendf( buf, "" ); } else { vips_buf_appendf( buf, "" ); } } else if( PEISTAG( base ) ) vips_buf_appendf( buf, ".%s", PEGETTAG( base ) ); else { vips_buf_appendf( buf, "<" ); vips_buf_appends( buf, _( "function" ) ); vips_buf_appendf( buf, ">" ); } return( TRUE ); } /* Little wrapper ... used for formatting error messages, etc. FALSE for eval * error. */ gboolean itext_value( Reduce *rc, VipsBuf *buf, PElement *root ) { /* Evaluate and print off values. */ if( !reduce_pelement( rc, reduce_spine, root ) ) return( FALSE ); if( !itext_add_element( buf, root, TRUE, FALSE ) && !buf->full ) /* Tally eval failed, and buffer is not full ... must * have been an eval error. */ return( FALSE ); return( TRUE ); } /* Same, but everror on eval fail. */ void itext_value_ev( Reduce *rc, VipsBuf *buf, PElement *root ) { if( !itext_value( rc, buf, root ) ) reduce_throw( rc ); } /* Decompile an Expr. */ static gboolean itext_make_decompiled_string( Expr *expr, VipsBuf *buf ) { /* Old error on this expression? */ if( expr->err ) { expr_error_get( expr ); return( FALSE ); } /* Dirty? We can't print dirty values, since we might have pointers * to deleted symbols in the heap (if we are dirty because one of our * parents has been deleted). FIXME ... this seem a bit restrictive :-( ... could just block reads of symbol pointers instead? */ if( expr->sym->dirty ) { vips_buf_appendf( buf, _( "Dirty value" ) ); return( TRUE ); } /* Evaluate and print off values. */ if( !itext_decompile( reduce_context, buf, &expr->root ) ) return( FALSE ); return( TRUE ); } /* Make a value string from an Expr. */ gboolean itext_make_value_string( Expr *expr, VipsBuf *buf ) { /* Old error on this expression? */ if( expr->err ) { expr_error_get( expr ); return( FALSE ); } /* Dirty? We can't print dirty values, since we might have pointers * to deleted symbols in the heap (if we are dirty because one of our * parents has been deleted). FIXME ... this seem a bit restrictive :-( ... could just block reads of symbol pointers instead? */ if( expr->sym->dirty ) { vips_buf_appendf( buf, _( "Dirty value" ) ); return( TRUE ); } /* Evaluate and print off values. */ if( !itext_value( reduce_context, buf, &expr->root ) ) return( FALSE ); return( TRUE ); } static void * itext_update_model( Heapmodel *heapmodel ) { iText *itext = ITEXT( heapmodel ); Row *row = HEAPMODEL( itext )->row; Expr *expr = row->expr; #ifdef DEBUG printf( "itext_update_model: " ); row_name_print( row ); if( row->sym && row->sym->dirty ) printf( " (dirty)" ); printf( "\n" ); #endif /*DEBUG*/ vips_buf_set_dynamic( &itext->value, LINELENGTH ); vips_buf_set_dynamic( &itext->decompile, LINELENGTH ); if( expr ) { if( !itext_make_value_string( expr, &itext->value ) || !itext_make_decompiled_string( expr, &itext->decompile ) ) expr_error_set( expr ); } #ifdef DEBUG printf( "itext_update_model: " ); row_name_print( row ); printf( " has value: %s\n", vips_buf_all( &itext->value ) ); #endif /*DEBUG*/ /* If this is a non-edited row, update the source. */ if( !itext->edited || row == row->top_row ) { const char *new_formula; if( expr && expr->compile && expr->compile->rhstext ) new_formula = expr->compile->rhstext; else new_formula = vips_buf_all( &itext->decompile ); IM_SETSTR( itext->formula_default, new_formula ); /* Don't use itext_set_formula(), as we don't want to set * _modified or recomp. */ IM_SETSTR( itext->formula, itext->formula_default ); } return( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ); } /* Build param lists. */ static void * itext_update_heap_sub( Symbol *sym, VipsBuf *buf ) { vips_buf_appendf( buf, "%s ", IOBJECT( sym )->name ); return( NULL ); } /* heapmodel->modified is set ... parse, compile, and mark for recomp. */ static void * itext_update_heap( Heapmodel *heapmodel ) { iText *itext = ITEXT( heapmodel ); Row *row = heapmodel->row; Expr *expr = row->expr; #ifdef DEBUG printf( "itext_update_heap: " ); row_name_print( HEAPMODEL( itext )->row ); printf( "\n" ); #endif /*DEBUG*/ /* We can have no modified text, but come here anyway. For example, we * could try eval, find an error due to an undefined symbol, and have * to retry later. Clearing the row error later will mark us modified, * even though we have no text of our own. */ if( itext->formula ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); ParseRhsSyntax syntax; if( row->sym && is_super( row->sym ) ) { /* A super member ... special syntax. */ vips_buf_appendf( &buf, "%s", itext->formula ); syntax = PARSE_SUPER; } else { /* Build a new params + '=' + rhs string. */ if( expr->compile ) (void) slist_map( expr->compile->param, (SListMapFn) itext_update_heap_sub, &buf ); vips_buf_appendf( &buf, "= %s;", itext->formula ); syntax = PARSE_PARAMS; } /* Parse and compile. */ expr_error_clear( expr ); attach_input_string( vips_buf_all( &buf ) ); if( !parse_rhs( expr, syntax ) ) { expr_error_set( expr ); return( heapmodel ); } } /* Mark for recomp. */ (void) expr_dirty( expr, link_serial_new() ); return( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) ); } static void * itext_clear_edited( Heapmodel *heapmodel ) { iText *itext = ITEXT( heapmodel ); #ifdef DEBUG printf( "itext_clear_edited: " ); row_name_print( HEAPMODEL( itext )->row ); printf( "\n" ); #endif /*DEBUG*/ if( itext->edited ) { itext_set_edited( itext, FALSE ); /* FIXME ... formula_default is not always set for cloned edited rows! fix this properly */ if( itext->formula_default ) itext_set_formula( itext, itext->formula_default ); else printf( "itext_clear_edited: FIXME!\n" ); if( heapmodel->row->expr ) expr_dirty( heapmodel->row->expr, link_serial_new() ); /* Don't clear HEAPMODEL( itext )->modified, we want to make * sure we re-parse and compile the default value to break any * old links we might have. */ } return( HEAPMODEL_CLASS( parent_class )->clear_edited( heapmodel ) ); } static void itext_parent_add( iContainer *child ) { iText *itext = ITEXT( child ); Row *row; g_assert( IS_RHS( child->parent ) ); ICONTAINER_CLASS( parent_class )->parent_add( child ); row = HEAPMODEL( itext )->row; #ifdef DEBUG printf( "itext_new: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ /* Top rows default to edited. */ if( row == row->top_row ) itext->edited = TRUE; } static gboolean itext_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { iText *itext = ITEXT( model ); char formula[MAX_STRSIZE]; char formula2[MAX_STRSIZE]; g_assert( IS_RHS( parent ) ); if( get_sprop( xnode, "formula", formula, MAX_STRSIZE ) ) { model_loadstate_rewrite( state, formula, formula2 ); itext_set_formula( itext, formula2 ); itext_set_edited( itext, TRUE ); } return( MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ); } static View * itext_view_new( Model *model, View *parent ) { return( itextview_new() ); } static xmlNode * itext_save( Model *model, xmlNode *xnode ) { iText *itext = ITEXT( model ); Row *row = HEAPMODEL( model )->row; xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( itext->edited || row->top_row == row ) if( !set_sprop( xthis, "formula", itext->formula ) ) return( NULL ); return( xthis ); } static void itext_class_init( iTextClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = itext_finalize; iobject_class->info = itext_info; icontainer_class->parent_add = itext_parent_add; model_class->view_new = itext_view_new; model_class->save = itext_save; model_class->load = itext_load; heapmodel_class->update_model = itext_update_model; heapmodel_class->update_heap = itext_update_heap; heapmodel_class->clear_edited = itext_clear_edited; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void itext_init( iText *itext ) { Model *model = MODEL( itext ); model->display = FALSE; itext->formula = NULL; itext->formula_default = NULL; vips_buf_init( &itext->value ); vips_buf_init( &itext->decompile ); vips_buf_set_dynamic( &itext->value, LINELENGTH ); vips_buf_set_dynamic( &itext->decompile, LINELENGTH ); itext->edited = FALSE; /* Some defaults changed in _parent_add() above. */ } GType itext_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( iTextClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) itext_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( iText ), 32, /* n_preallocs */ (GInstanceInitFunc) itext_init, }; type = g_type_register_static( TYPE_HEAPMODEL, "iText", &info, 0 ); } return( type ); } iText * itext_new( Rhs *rhs ) { iText *itext; itext = ITEXT( g_object_new( TYPE_ITEXT, NULL ) ); icontainer_child_add( ICONTAINER( rhs ), ICONTAINER( itext ), -1 ); return( itext ); } void itext_set_edited( iText *itext, gboolean edited ) { Heapmodel *heapmodel = HEAPMODEL( itext ); if( itext->edited != edited ) { #ifdef DEBUG printf( "itext_set_edited: " ); row_name_print( heapmodel->row ); printf( " %s\n", bool_to_char( edited ) ); #endif /*DEBUG*/ itext->edited = edited; iobject_changed( IOBJECT( itext ) ); } if( edited ) heapmodel_set_modified( heapmodel, TRUE ); } gboolean itext_set_formula( iText *itext, const char *formula ) { if( !itext->formula || strcmp( itext->formula, formula ) != 0 ) { #ifdef DEBUG printf( "itext_set_formula: " ); row_name_print( HEAPMODEL( itext )->row ); printf( " \"%s\"\n", formula ); #endif /*DEBUG*/ IM_SETSTR( itext->formula, formula ); heapmodel_set_modified( HEAPMODEL( itext ), TRUE ); iobject_changed( IOBJECT( itext ) ); return( TRUE ); } return( FALSE ); } ================================================ FILE: src/itext.h ================================================ /* a text button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_ITEXT (itext_get_type()) #define ITEXT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_ITEXT, iText )) #define ITEXT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_ITEXT, iTextClass)) #define IS_ITEXT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_ITEXT )) #define IS_ITEXT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_ITEXT )) #define ITEXT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_ITEXT, iTextClass )) struct _iText { Heapmodel parent_class; VipsBuf value; /* The value displayed as a [char] */ char *formula; /* The formula we edit */ char *formula_default; /* Formula we inherit */ VipsBuf decompile; /* The value decompiled to a [char] */ /* TRUE if the formula has been entered by the user and should be * saved. * * Can't use classmodel edited, as text must inherit from heapmodel. * Some duplication of code ... see itext_clear_edited() */ gboolean edited; }; typedef struct _iTextClass { HeapmodelClass parent_class; /* My methods. */ } iTextClass; GType itext_get_type( void ); iText *itext_new( Rhs *rhs ); gboolean itext_value( Reduce *rc, VipsBuf *buf, PElement *root ); void itext_value_ev( Reduce *rc, VipsBuf *buf, PElement *root ); gboolean itext_make_value_string( Expr *expr, VipsBuf *buf ); void itext_set_edited( iText *text, gboolean edited ); gboolean itext_set_formula( iText *text, const char *formula ); ================================================ FILE: src/itextview.c ================================================ /* a view of a text thingy */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static void itextview_refresh( vObject *vobject ) { iTextview *itextview = ITEXTVIEW( vobject ); iText *itext = ITEXT( VOBJECT( itextview )->iobject ); Row *row = HEAPMODEL( itext )->row; const char *display; #ifdef DEBUG printf( "itextview_refresh: " ); row_name_print( row ); printf( " (%p)\n", vobject ); #endif /*DEBUG*/ /* Only reset edit mode if the text hasn't been changed. We * don't want the user to lose work. */ if( !itextview->formula->changed ) switch( row->ws->mode ) { case WORKSPACE_MODE_REGULAR: formula_set_edit( itextview->formula, FALSE ); formula_set_sensitive( itextview->formula, TRUE ); break; case WORKSPACE_MODE_FORMULA: formula_set_edit( itextview->formula, TRUE ); formula_set_sensitive( itextview->formula, TRUE ); break; case WORKSPACE_MODE_NOEDIT: formula_set_edit( itextview->formula, FALSE ); formula_set_sensitive( itextview->formula, FALSE ); break; default: g_assert_not_reached(); } /* We display the formula if this is a class ... we assume the members * and/or the graphic will represent the value. */ if( row->is_class ) display = itext->formula; else display = vips_buf_all( &itext->value ); if( itextview->formula && itext->value.base ) formula_set_value_expr( itextview->formula, display, itext->formula ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void itextview_link( View *view, Model *model, View *parent ) { iTextview *itextview = ITEXTVIEW( view ); iText *itext = ITEXT( model ); Row *row = HEAPMODEL( itext )->row; #ifdef DEBUG printf( "itextview_link: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ VIEW_CLASS( parent_class )->link( view, model, parent ); /* Edit mode defaults to edit mode for workspace. */ formula_set_edit( itextview->formula, row->ws->mode == WORKSPACE_MODE_FORMULA ); } /* Reset edit mode ... go back to whatever is set for this ws. */ static void itextview_reset( View *view ) { iTextview *itextview = ITEXTVIEW( view ); iText *itext = ITEXT( VOBJECT( itextview )->iobject ); Row *row = HEAPMODEL( itext )->row; #ifdef DEBUG printf( "itextview_reset: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ formula_set_edit( ITEXTVIEW( view )->formula, row->ws->mode == WORKSPACE_MODE_FORMULA ); VIEW_CLASS( parent_class )->reset( view ); } /* Re-read the text in a tally entry. */ static void * itextview_scan( View *view ) { iTextview *itextview = ITEXTVIEW( view ); iText *itext = ITEXT( VOBJECT( itextview )->iobject ); #ifdef DEBUG Row *row = HEAPMODEL( itext )->row; printf( "itextview_scan: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ if( formula_scan( itextview->formula ) && itext_set_formula( itext, itextview->formula->expr ) ) itext_set_edited( itext, TRUE ); return( VIEW_CLASS( parent_class )->scan( view ) ); } static void itextview_class_init( iTextviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = itextview_refresh; view_class->link = itextview_link; view_class->reset = itextview_reset; view_class->scan = itextview_scan; } void itextview_edit_cb( Formula *formula, iTextview *itextview ) { view_resettable_register( VIEW( itextview ) ); } void itextview_activate_cb( Formula *formula, iTextview *itextview ) { iText *itext = ITEXT( VOBJECT( itextview )->iobject ); Row *row = HEAPMODEL( itext )->row; /* Reset edits on this row and all children. Our (potentially) next * text will invlidate all of them. */ (void) icontainer_map_all( ICONTAINER( row ), (icontainer_map_fn) heapmodel_clear_edited, NULL ); /* Make sure we scan this text, even if it's not been edited. */ view_scannable_register( VIEW( itextview ) ); workspace_set_modified( row->ws, TRUE ); symbol_recalculate_all(); } static void itextview_enter_cb( Formula *formula, iTextview *itextview ) { iText *itext = ITEXT( VOBJECT( itextview )->iobject ); Row *row = HEAPMODEL( itext )->row; row_set_status( row ); row_show_dependents( row ); } static void itextview_leave_cb( Formula *formula, iTextview *itextview ) { iText *itext = ITEXT( VOBJECT( itextview )->iobject ); Row *row = HEAPMODEL( itext )->row; row_hide_dependents( row ); } static void itextview_init( iTextview *itextview ) { itextview->formula = formula_new(); gtk_signal_connect( GTK_OBJECT( itextview->formula ), "edit", GTK_SIGNAL_FUNC( itextview_edit_cb ), itextview ); gtk_signal_connect_object( GTK_OBJECT( itextview->formula ), "changed", GTK_SIGNAL_FUNC( view_changed_cb ), itextview ); gtk_signal_connect( GTK_OBJECT( itextview->formula ), "activate", GTK_SIGNAL_FUNC( itextview_activate_cb ), itextview ); gtk_signal_connect( GTK_OBJECT( itextview->formula ), "enter", GTK_SIGNAL_FUNC( itextview_enter_cb ), itextview ); gtk_signal_connect( GTK_OBJECT( itextview->formula ), "leave", GTK_SIGNAL_FUNC( itextview_leave_cb ), itextview ); gtk_box_pack_start( GTK_BOX( itextview ), GTK_WIDGET( itextview->formula ), TRUE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( itextview->formula ) ); } GtkType itextview_get_type( void ) { static GtkType itextview_type = 0; if( !itextview_type ) { static const GtkTypeInfo itextview_info = { "iTextview", sizeof( iTextview ), sizeof( iTextviewClass ), (GtkClassInitFunc) itextview_class_init, (GtkObjectInitFunc) itextview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; itextview_type = gtk_type_unique( TYPE_VIEW, &itextview_info ); } return( itextview_type ); } View * itextview_new( void ) { iTextview *itextview = gtk_type_new( TYPE_ITEXTVIEW ); return( VIEW( itextview ) ); } ================================================ FILE: src/itextview.h ================================================ /* a textview button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_ITEXTVIEW (itextview_get_type()) #define ITEXTVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_ITEXTVIEW, iTextview )) #define ITEXTVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_ITEXTVIEW, iTextviewClass )) #define IS_ITEXTVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_ITEXTVIEW )) #define IS_ITEXTVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_ITEXTVIEW )) typedef struct _iTextview { View view; /* Widgets. */ Formula *formula; } iTextview; typedef struct _iTextviewClass { ViewClass parent_class; /* My methods. */ } iTextviewClass; GtkType itextview_get_type( void ); View *itextview_new( void ); ================================================ FILE: src/iwindow.c ================================================ /* make and manage base windows ... dialog (messagebox, file box), top * level windows */ /* 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 General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* build interface: iwnd = iwindow_new( type ); iwindow_set_*( iwnd, ... ); iwindow_build( iwnd ); destroy interface: iwindow_kill() 'cancellable' kill ... user popdown can return IWINDOW_ERROR or IWINDOW_NO to prevent popdown gtk_widget_destroy() non-cancellable ... popdown is not called so ... don't free() in popdown, subclass iwnd and free() in _destroy() */ /* #define DEBUG */ #include "ip.h" /* Cursor bitmaps. */ #include "BITMAPS/dropper_src.xbm" #include "BITMAPS/dropper_msk.xbm" #include "BITMAPS/magin_src.xbm" #include "BITMAPS/magout_src.xbm" #include "BITMAPS/mag_msk.xbm" #include "BITMAPS/watch_1.xbm" #include "BITMAPS/watch_2.xbm" #include "BITMAPS/watch_3.xbm" #include "BITMAPS/watch_4.xbm" #include "BITMAPS/watch_5.xbm" #include "BITMAPS/watch_6.xbm" #include "BITMAPS/watch_7.xbm" #include "BITMAPS/watch_8.xbm" #include "BITMAPS/watch_msk.xbm" static GtkWindowClass *parent_class = NULL; /* List of all iwindows. */ static GSList *iwindow_all = NULL; /* All our cursors. */ static GdkCursor *iwindow_cursor[IWINDOW_SHAPE_LAST] = { NULL }; #ifdef DEBUG /* Human-readable names for cursor shapes. */ static const char *iwindow_cursor_name[] = { "IWINDOW_SHAPE_DROPPER", "IWINDOW_SHAPE_PEN", "IWINDOW_SHAPE_SMUDGE", "IWINDOW_SHAPE_SMEAR", "IWINDOW_SHAPE_TEXT", "IWINDOW_SHAPE_RECT", "IWINDOW_SHAPE_FLOOD", "IWINDOW_SHAPE_MOVE", "IWINDOW_SHAPE_EDIT", "IWINDOW_SHAPE_MAGIN", "IWINDOW_SHAPE_MAGOUT", "IWINDOW_SHAPE_TOP", "IWINDOW_SHAPE_BOTTOM", "IWINDOW_SHAPE_LEFT", "IWINDOW_SHAPE_RIGHT", "IWINDOW_SHAPE_TOPRIGHT", "IWINDOW_SHAPE_TOPLEFT", "IWINDOW_SHAPE_BOTTOMRIGHT", "IWINDOW_SHAPE_BOTTOMLEFT", "IWINDOW_SHAPE_HGLASS1", "IWINDOW_SHAPE_HGLASS2", "IWINDOW_SHAPE_HGLASS3", "IWINDOW_SHAPE_HGLASS4", "IWINDOW_SHAPE_HGLASS5", "IWINDOW_SHAPE_HGLASS6", "IWINDOW_SHAPE_HGLASS7", "IWINDOW_SHAPE_HGLASS8", "IWINDOW_SHAPE_NONE" }; #endif /*DEBUG*/ int iwindow_number( void ) { return( g_slist_length( iwindow_all ) ); } /* Pick an iwindow at random. Used if we need a window for a dialog, and we're * not sure which to pick. During shutdown we can have no windows. */ iWindow * iwindow_pick_one( void ) { if( !iwindow_all ) return( NULL ); return( IWINDOW( iwindow_all->data ) ); } /* Over all windows. */ void * iwindow_map_all( iWindowMapFn fn, void *a ) { return( slist_map( iwindow_all, (SListMapFn) fn, a ) ); } /* Make a custom cursor ... source, mask, width, height and hot spot position. */ static GdkCursor * iwindow_make_cursor_data( guchar *src_bits, guchar *msk_bits, int w, int h, int x, int y ) { GdkPixmap *src; GdkPixmap *msk; GdkCursor *cursor; GdkColor fg = { 0, 255 << 8, 255 << 8, 255 << 8 }; GdkColor bg = { 0, 0, 0, 0 }; src = gdk_bitmap_create_from_data( NULL, (const char *) src_bits, w, h ); msk = gdk_bitmap_create_from_data( NULL, (const char *) msk_bits, w, h ); cursor = gdk_cursor_new_from_pixmap( src, msk, &fg, &bg, x, y ); gdk_pixmap_unref( src ); gdk_pixmap_unref( msk ); return( cursor ); } /* Build all the cursors. */ static void iwindow_make_cursors( void ) { /* Init standard cursors with this table. */ static GdkCursorType standards[] = { GDK_CURSOR_IS_PIXMAP, /* IWINDOW_SHAPE_DROPPER */ GDK_PENCIL, /* IWINDOW_SHAPE_PEN */ GDK_HAND2, /* IWINDOW_SHAPE_SMUDGE */ GDK_SPIDER, /* IWINDOW_SHAPE_SMEAR */ GDK_GOBBLER, /* IWINDOW_SHAPE_TEXT */ GDK_SIZING, /* IWINDOW_SHAPE_RECT */ GDK_TREK, /* IWINDOW_SHAPE_FLOOD */ GDK_FLEUR, /* IWINDOW_SHAPE_MOVE */ GDK_CROSSHAIR, /* IWINDOW_SHAPE_EDIT */ GDK_CURSOR_IS_PIXMAP, /* IWINDOW_SHAPE_MAGIN */ GDK_CURSOR_IS_PIXMAP, /* IWINDOW_SHAPE_MAGOUT */ GDK_TOP_SIDE, /* IWINDOW_SHAPE_TOP */ GDK_BOTTOM_SIDE, /* IWINDOW_SHAPE_BOTTOM */ GDK_LEFT_SIDE, /* IWINDOW_SHAPE_LEFT */ GDK_RIGHT_SIDE, /* IWINDOW_SHAPE_RIGHT */ GDK_TOP_RIGHT_CORNER, /* IWINDOW_SHAPE_TOPRIGHT */ GDK_TOP_LEFT_CORNER, /* IWINDOW_SHAPE_TOPLEFT */ GDK_BOTTOM_RIGHT_CORNER,/* IWINDOW_SHAPE_BOTTOMRIGHT, */ GDK_BOTTOM_LEFT_CORNER, /* IWINDOW_SHAPE_BOTTOMLEFT */ }; /* All the bits for the rotating cursor. */ static guchar *watch_bits[] = { watch_1_bits, watch_2_bits, watch_3_bits, watch_4_bits, watch_5_bits, watch_6_bits, watch_7_bits, watch_8_bits, }; int i; if( iwindow_cursor[0] ) return; /* Easy ones first. */ for( i = 0; i < IM_NUMBER( standards ); i++ ) if( standards[i] != GDK_CURSOR_IS_PIXMAP ) iwindow_cursor[i] = gdk_cursor_new( standards[i] ); /* Custom cursors. */ iwindow_cursor[IWINDOW_SHAPE_DROPPER] = iwindow_make_cursor_data( dropper_src_bits, dropper_msk_bits, dropper_src_width, dropper_src_height, 0, 15 ); iwindow_cursor[IWINDOW_SHAPE_MAGIN] = iwindow_make_cursor_data( magin_src_bits, mag_msk_bits, mag_msk_width, mag_msk_height, 6, 6 ); iwindow_cursor[IWINDOW_SHAPE_MAGOUT] = iwindow_make_cursor_data( magout_src_bits, mag_msk_bits, mag_msk_width, mag_msk_height, 6, 6 ); /* The hglasses. */ for( i = 0; i < IM_NUMBER( watch_bits ); i++ ) iwindow_cursor[IWINDOW_SHAPE_HGLASS1 + i] = iwindow_make_cursor_data( watch_bits[i], watch_msk_bits, watch_1_width, watch_1_height, 7, 7 ); } /* Get the work window. */ static GdkWindow * iwindow_get_work_window( iWindow *iwnd ) { if( iwnd->work_window ) return( iwnd->work_window ); else return( GTK_WIDGET( iwnd )->window ); } /* Update the cursor for a window. */ static void * iwindow_cursor_update( iWindow *iwnd ) { if( GTK_WIDGET_REALIZED( GTK_WIDGET( iwnd ) ) ) { GSList *p; iWindowShape best_shape; int best_priority; /* Global shape set? Use that for the whole window. */ if( iwnd->shape != IWINDOW_SHAPE_NONE ) { gdk_window_set_cursor( GTK_WIDGET( iwnd )->window, iwindow_cursor[iwnd->shape] ); gdk_window_set_cursor( iwindow_get_work_window( iwnd ), iwindow_cursor[iwnd->shape] ); gdk_flush(); return( NULL ); } /* No global shape ... make sure there's no global cursor on * this window. */ gdk_window_set_cursor( GTK_WIDGET( iwnd )->window, NULL ); gdk_window_set_cursor( iwindow_get_work_window( iwnd ), NULL ); /* And set the work area to the highest priority non-NONE * shape we can find . FIXME ... could avoid the search if we sorted the context list by priority on each context_new(), but not very important. */ best_shape = IWINDOW_SHAPE_NONE; best_priority = -1; for( p = iwnd->contexts; p; p = p->next ) { iWindowCursorContext *cntxt = (iWindowCursorContext *) p->data; if( cntxt->shape != IWINDOW_SHAPE_NONE && cntxt->priority > best_priority ) { best_shape = cntxt->shape; best_priority = cntxt->priority; } } /* Pref to disable crosshair. */ if( best_shape == IWINDOW_SHAPE_EDIT && !DISPLAY_CROSSHAIR ) best_shape = IWINDOW_SHAPE_NONE; gdk_window_set_cursor( iwindow_get_work_window( iwnd ), iwindow_cursor[best_shape] ); gdk_flush(); } return( NULL ); } /* Set a global cursor for a window. */ static void * iwindow_cursor_set( iWindow *iwnd, iWindowShape *shape ) { if( iwnd->shape != *shape ) { iwnd->shape = *shape; iwindow_cursor_update( iwnd ); } return( NULL ); } static gboolean hourglass_showing = FALSE; static void hourglass_begin( void ) { hourglass_showing = TRUE; } static void hourglass_update( void ) { if( hourglass_showing ) { static iWindowShape shape = IWINDOW_SHAPE_HGLASS1; iwindow_map_all( (iWindowMapFn) iwindow_cursor_set, &shape ); shape += 1; if( shape > IWINDOW_SHAPE_HGLASS8 ) shape = IWINDOW_SHAPE_HGLASS1; } } static void hourglass_end( void ) { if( hourglass_showing ) { iWindowShape shape = IWINDOW_SHAPE_NONE; iwindow_map_all( (iWindowMapFn) iwindow_cursor_set, &shape ); hourglass_showing = FALSE; } } iWindowCursorContext * iwindow_cursor_context_new( iWindow *iwnd, int priority, const char *name ) { iWindowCursorContext *cntxt = INEW( NULL, iWindowCursorContext ); #ifdef DEBUG printf( "iwindow_cursor_context_new: %s\n", name ); #endif /*DEBUG*/ cntxt->iwnd = iwnd; cntxt->priority = priority; cntxt->name = name; cntxt->shape = IWINDOW_SHAPE_NONE; iwnd->contexts = g_slist_prepend( iwnd->contexts, cntxt ); return( cntxt ); } void iwindow_cursor_context_destroy( iWindowCursorContext *cntxt ) { iWindow *iwnd = cntxt->iwnd; iwnd->contexts = g_slist_remove( iwnd->contexts, cntxt ); IM_FREE( cntxt ); iwindow_cursor_update( iwnd ); } void iwindow_cursor_context_set_cursor( iWindowCursorContext *cntxt, iWindowShape shape ) { if( cntxt->shape != shape ) { #ifdef DEBUG printf( "iwindow_cursor_context_set_cursor: %s = %s\n", cntxt->name, iwindow_cursor_name[shape] ); #endif /*DEBUG*/ cntxt->shape = shape; iwindow_cursor_update( cntxt->iwnd ); } } iWindowSusp * iwindow_susp_new( iWindowFn fn, iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { iWindowSusp *susp; if( !(susp = INEW( NULL, iWindowSusp )) ) return( NULL ); susp->fn = fn; susp->iwnd = iwnd; susp->client = client; susp->nfn = nfn; susp->sys = sys; return( susp ); } /* Trigger a suspension's reply, and free it. */ void iwindow_susp_return( void *sys, iWindowResult result ) { iWindowSusp *susp = IWINDOW_SUSP( sys ); susp->nfn( susp->sys, result ); im_free( susp ); } void iwindow_susp_trigger( iWindowSusp *susp ) { susp->fn( susp->iwnd, susp->client, susp->nfn, susp->sys ); im_free( susp ); } /* Compose two iWindowFns ... if this one succeeded, trigger the next in turn. * Otherwise bail out. */ void iwindow_susp_comp( void *sys, iWindowResult result ) { iWindowSusp *susp = IWINDOW_SUSP( sys ); if( result == IWINDOW_YES ) iwindow_susp_trigger( susp ); else iwindow_susp_return( sys, result ); } /* Null window callback. */ void iwindow_true_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { nfn( sys, IWINDOW_YES ); } void iwindow_false_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { nfn( sys, IWINDOW_NO ); } /* Null notify callback. */ void iwindow_notify_null( void *client, iWindowResult result ) { } /* Final end of a window. Destroy! */ static void iwindow_final_death( iWindow *iwnd ) { #ifdef DEBUG printf( "iwindow_final_death: %s\n", iwnd->title ); #endif /*DEBUG*/ g_assert( iwnd->pending == 0 && iwnd->destroy ); /* Clean up. */ gtk_widget_destroy( GTK_WIDGET( iwnd ) ); } /* A notify comes back ... adjust the pending count. If this is a zombie and * this is the final pending, it's final death. */ void iwindow_notify_return( iWindow *iwnd ) { #ifdef DEBUG printf( "iwindow_notify_return: %s (pending = %d)\n", iwnd->title, iwnd->pending ); #endif /*DEBUG*/ g_assert( iwnd->pending > 0 ); iwnd->pending--; if( iwnd->destroy && iwnd->pending == 0 ) { #ifdef DEBUG printf( "iwindow_notify_return: zombie death %s\n", iwnd->title ); #endif /*DEBUG*/ iwindow_final_death( iwnd ); } } /* Send a notify off, tell the client to come back to back. */ void iwindow_notify_send( iWindow *iwnd, iWindowFn fn, void *client, iWindowNotifyFn back, void *sys ) { #ifdef DEBUG printf( "iwindow_notify_send: %s (pending = %d)\n", iwnd->title, iwnd->pending ); #endif /*DEBUG*/ iwnd->pending++; if( fn ) fn( iwnd, client, back, sys ); else back( sys, IWINDOW_YES ); } static void iwindow_finalize( GObject *gobject ) { iWindow *iwnd = IWINDOW( gobject ); #ifdef DEBUG printf( "iwindow_finalize: %s\n", iwnd->title ); #endif /*DEBUG*/ /* My instance destroy stuff. */ iwindow_all = g_slist_remove( iwindow_all, iwnd ); IM_FREE( iwnd->title ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); /* Last window and we've got through startup? Quit the application. */ if( iwindow_number() == 0 && !main_starting ) main_quit_test(); } static void iwindow_destroy( GtkObject *gobject ) { iWindow *iwnd = IWINDOW( gobject ); #ifdef DEBUG printf( "iwindow_destroy: %s\n", iwnd->title ); #endif /*DEBUG*/ /* My instance destroy stuff. */ FREESID( iwnd->parent_unmap_sid, iwnd->parent_window ); UNREF( iwnd->action_group ); UNREF( iwnd->ui_manager ); /* Now we've destroyed, we must stop popdown from being called, since * the view will have junked a lot of stuff. */ iwnd->destroy = TRUE; GTK_OBJECT_CLASS( parent_class )->destroy( gobject ); } static void iwindow_popdown_notify( iWindow *iwnd, iWindowResult result ) { #ifdef DEBUG printf( "iwindow_popdown_notify: %p %s\n", iwnd, iwnd->title ); #endif /*DEBUG*/ if( result == IWINDOW_ERROR ) iwindow_alert( GTK_WIDGET( iwnd ), GTK_MESSAGE_ERROR ); else if( result == IWINDOW_YES ) iwindow_kill( iwnd ); if( result != IWINDOW_YES ) { #ifdef DEBUG printf( "iwindow_popdown_notify: %s: kill cancelled!\n", iwnd->title ); #endif /*DEBUG*/ /* Cancel popdown. */ iwnd->destroy = FALSE; } else { /* Popdown confirmed! Trigger class popdown. _real_popdown() * does an unmap to hide the window during the rest of the * destroy. */ IWINDOW_GET_CLASS( iwnd )->popdown( GTK_WIDGET( iwnd ) ); } calli_string_filenamef( (calli_string_fn) gtk_accel_map_save, "%s" G_DIR_SEPARATOR_S "accel_map", get_savedir() ); /* If this is the final pending response and ->destroy is true, this * will destroy the window. */ iwindow_notify_return( iwnd ); } static gboolean iwindow_delete_event( GtkWidget *widget, GdkEventAny *event ) { iWindow *iwnd = IWINDOW( widget ); #ifdef DEBUG printf( "iwindow_delete_event: %s\n", iwnd->title ); #endif /*DEBUG*/ if( !iwnd->destroy ) { #ifdef DEBUG printf( "iwindow_delete_event: starting destroy\n" ); #endif /*DEBUG*/ iwindow_kill( iwnd ); } /* Never delete here ... wait for iwindow_popdown_notify to * confirm the kill. */ return( TRUE ); } static gboolean iwindow_configure_event( GtkWidget *widget, GdkEventConfigure *event ) { iWindow *iwnd = IWINDOW( widget ); if( iwnd->width_pref ) { /* Save window size in global prefs. */ prefs_set( iwnd->width_pref, "%d", event->width ); prefs_set( iwnd->height_pref, "%d", event->height ); } return( GTK_WIDGET_CLASS( parent_class )-> configure_event( widget, event ) ); } /* Our parent has been destroyed, kill us too. */ static void iwindow_parent_unmap_cb( GtkWidget *par, iWindow *iwnd ) { #ifdef DEBUG printf( "iwindow_parent_unmap_cb: %s\n", iwnd->title ); #endif /*DEBUG*/ /* Here for dead parent ... if parent is dead, we won't need to remove * the dead-dad signal. */ iwnd->parent_unmap_sid = 0; iwindow_kill( iwnd ); } static GtkActionEntry iwnd_actions[] = { /* Common menus. */ { "FileMenu", NULL, N_( "_File" ) }, { "NewMenu", NULL, N_( "_New" ) }, { "EditMenu", NULL, N_( "_Edit" ) }, { "ViewMenu", NULL, N_( "_View" ) }, { "HelpMenu", NULL, N_( "_Help" ) }, /* Common items. */ { "Close", GTK_STOCK_CLOSE, N_( "_Close" ), NULL, N_( "Close" ), G_CALLBACK( iwindow_kill_action_cb ) }, { "Quit", GTK_STOCK_QUIT, N_( "_Quit" ), "q", N_( "Quit nip2" ), G_CALLBACK( main_quit_test ) }, { "Guide", GTK_STOCK_HELP, N_( "_Contents" ), "F1", N_( "Open the users guide" ), G_CALLBACK( mainw_guide_action_cb ) }, { "About", NULL, N_( "_About" ), NULL, N_( "About this program" ), G_CALLBACK( mainw_about_action_cb ) }, { "Homepage", NULL, N_( "_Website" ), NULL, N_( "Open the VIPS Homepage" ), G_CALLBACK( mainw_homepage_action_cb ) } }; static void iwindow_real_build( GtkWidget *widget ) { iWindow *iwnd = IWINDOW( widget ); GdkScreen *screen = gtk_widget_get_screen( GTK_WIDGET( iwnd ) ); GtkAccelGroup *accel_group; #ifdef DEBUG printf( "iwindow_real_build: %s\n", iwnd->title ); #endif /*DEBUG*/ gtk_container_set_border_width( GTK_CONTAINER( iwnd ), 0 ); iwnd->work = gtk_vbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( iwnd ), iwnd->work ); /* Use the type name (eg. "Imageview") for the name of the * actiongroup. */ iwnd->action_group = gtk_action_group_new( G_OBJECT_TYPE_NAME( iwnd ) ); gtk_action_group_set_translation_domain( iwnd->action_group, GETTEXT_PACKAGE ); gtk_action_group_add_actions( iwnd->action_group, iwnd_actions, G_N_ELEMENTS( iwnd_actions ), GTK_WINDOW( iwnd ) ); iwnd->ui_manager = gtk_ui_manager_new(); gtk_ui_manager_insert_action_group( iwnd->ui_manager, iwnd->action_group, 0 ); accel_group = gtk_ui_manager_get_accel_group( iwnd->ui_manager ); gtk_window_add_accel_group( GTK_WINDOW( iwnd ), accel_group ); /* Call per-instance build. */ if( iwnd->build ) iwnd->build( iwnd, iwnd->work, iwnd->build_a, iwnd->build_b, iwnd->build_c ); if( iwnd->title ) gtk_window_set_title( GTK_WINDOW( iwnd ), iwnd->title ); if( iwnd->width_pref ) { int width = watch_int_get( main_watchgroup, iwnd->width_pref, 640 ); int height = watch_int_get( main_watchgroup, iwnd->height_pref, 480 ); gtk_window_set_default_size( GTK_WINDOW( iwnd ), IM_MIN( width, gdk_screen_get_width( screen ) ), IM_MIN( height, gdk_screen_get_height( screen ) ) ); } /* Link to parent. */ if( iwnd->parent_window ) { if( IWINDOW_GET_CLASS( iwnd )->transient && iwnd->parent_window && iwnd != iwnd->parent_window ) gtk_window_set_transient_for( GTK_WINDOW( iwnd ), GTK_WINDOW( iwnd->parent_window ) ); /* We watch our parent's "unmap" rather than "destroy" since * we use gtk_widget_unmap() to hide killed windows during * popdown (see iwindow_popdown_notify()). */ iwnd->parent_unmap_sid = gtk_signal_connect( GTK_OBJECT( iwnd->parent_window ), "unmap", GTK_SIGNAL_FUNC( iwindow_parent_unmap_cb ), iwnd ); /* Show the parent. For example, if this is the ^Q * save-or-quit dialog and the parent is a mainw, we want to * pop the mainw up. */ gtk_window_present( GTK_WINDOW( iwnd->parent_window ) ); } gtk_widget_show( iwnd->work ); } static void iwindow_real_popdown( GtkWidget *widget ) { gtk_widget_unmap( widget ); } static void iwindow_class_init( iWindowClass *class ) { GObjectClass *object_class = (GObjectClass *) class; GtkObjectClass *gobject_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; parent_class = g_type_class_peek_parent( class ); /* Init methods. */ object_class->finalize = iwindow_finalize; gobject_class->destroy = iwindow_destroy; widget_class->delete_event = iwindow_delete_event; widget_class->configure_event = iwindow_configure_event; class->build = iwindow_real_build; class->popdown = iwindow_real_popdown; class->transient = FALSE; /* Create signals. */ /* Static class data init. */ iwindow_make_cursors(); /* Link to busy signals. */ g_signal_connect( progress_get(), "begin", hourglass_begin, NULL ); g_signal_connect( progress_get(), "update", hourglass_update, NULL ); g_signal_connect( progress_get(), "end", hourglass_end, NULL ); } static void iwindow_init( iWindow *iwnd ) { #ifdef DEBUG printf( "iwindow_init: %s\n", iwnd->title ); #endif /*DEBUG*/ iwnd->work = NULL; iwnd->parent = NULL; iwnd->parent_window = NULL; iwnd->parent_unmap_sid = 0; /* Might as well. */ iwnd->accel_group = gtk_accel_group_new(); gtk_window_add_accel_group( GTK_WINDOW( iwnd ), iwnd->accel_group ); g_object_unref( iwnd->accel_group ); iwnd->infobar = NULL; iwnd->title = NULL; iwnd->build = NULL; iwnd->popdown = iwindow_true_cb; iwnd->destroy = FALSE; iwnd->pending = 0; iwnd->shape = IWINDOW_SHAPE_NONE; iwnd->contexts = NULL; iwnd->work_window = NULL; iwnd->width_pref = NULL; iwnd->height_pref = NULL; iwindow_all = g_slist_prepend( iwindow_all, iwnd ); } GtkType iwindow_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "iWindow", sizeof( iWindow ), sizeof( iWindowClass ), (GtkClassInitFunc) iwindow_class_init, (GtkObjectInitFunc) iwindow_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( GTK_TYPE_WINDOW, &info ); } return( type ); } GtkWidget * iwindow_new( GtkWindowType type ) { iWindow *iwnd = gtk_type_new( TYPE_IWINDOW ); GtkWindow *gwnd = GTK_WINDOW( iwnd ); /* Init superclass. */ gwnd->type = type; return( GTK_WIDGET( iwnd ) ); } void iwindow_set_title( iWindow *iwnd, const char *title, ... ) { va_list ap; char buf[1024]; va_start( ap, title ); (void) im_vsnprintf( buf, 1024, title, ap ); va_end( ap ); if( !iwnd->title || strcmp( iwnd->title, buf ) != 0 ) { IM_SETSTR( iwnd->title, buf ); gtk_window_set_title( GTK_WINDOW( iwnd ), iwnd->title ); } } void iwindow_set_build( iWindow *iwnd, iWindowBuildFn build, void *build_a, void *build_b, void *build_c ) { iwnd->build = build; iwnd->build_a = build_a; iwnd->build_b = build_b; iwnd->build_c = build_c; } void iwindow_set_popdown( iWindow *iwnd, iWindowFn popdown, void *popdown_a ) { iwnd->popdown = popdown; iwnd->popdown_a = popdown_a; } void iwindow_set_size_prefs( iWindow *iwnd, const char *width_pref, const char *height_pref ) { iwnd->width_pref = width_pref; iwnd->height_pref = height_pref; } void iwindow_set_work_window( iWindow *iwnd, GdkWindow *work_window ) { iwnd->work_window = work_window; iwindow_cursor_update( iwnd ); } void iwindow_set_parent( iWindow *iwnd, GtkWidget *parent ) { g_assert( !iwnd->parent ); iwnd->parent = parent; /* Get parent window now, we sometimes need it after parent has been * destroyed. */ if( parent ) iwnd->parent_window = IWINDOW( iwindow_get_root( GTK_WIDGET( parent ) ) ); } void * iwindow_kill( iWindow *iwnd ) { #ifdef DEBUG printf( "iwindow_kill: %p %s\n", iwnd, iwnd->title ); #endif /*DEBUG*/ if( !iwnd->destroy ) { #ifdef DEBUG printf( "... starting destroy for %s\n", iwnd->title ); #endif /*DEBUG*/ iwnd->destroy = TRUE; /* Don't kill directly, wait for popdown_notify to do it. */ iwindow_notify_send( iwnd, iwnd->popdown, iwnd->popdown_a, (iWindowNotifyFn) iwindow_popdown_notify, iwnd ); } return( NULL ); } /* ... as an action. */ void iwindow_kill_action_cb( GtkAction *action, iWindow *iwnd ) { iwindow_kill( iwnd ); } void iwindow_build( iWindow *iwnd ) { #ifdef DEBUG printf( "iwindow_build: %s\n", iwnd->title ); #endif /*DEBUG*/ IWINDOW_GET_CLASS( iwnd )->build( GTK_WIDGET( iwnd ) ); } /* Get the enclosing window for a widget. */ GtkWidget * iwindow_get_root( GtkWidget *widget ) { GtkWidget *toplevel = gtk_widget_get_toplevel( widget ); GtkWidget *child = gtk_bin_get_child( GTK_BIN( toplevel ) ); /* If this is a menu pane, get the widget that popped this menu up. */ if( GTK_IS_MENU( child ) ) { GtkWidget *parent = gtk_menu_get_attach_widget( GTK_MENU( child ) ); return( iwindow_get_root( parent ) ); } else return( toplevel ); } /* Get the enclosing no-parent window for a widget. */ GtkWidget * iwindow_get_root_noparent( GtkWidget *widget ) { GtkWidget *toplevel = iwindow_get_root( widget ); /* If this is a transient, get the window we popped up from. */ if( IS_IWINDOW( toplevel ) && IWINDOW( toplevel )->parent ) return( iwindow_get_root_noparent( IWINDOW( toplevel )->parent ) ); else return( toplevel ); } void iwindow_alert( GtkWidget *parent, GtkMessageType type ) { GtkWidget *toplevel; if( !parent ) parent = GTK_WIDGET( mainw_pick_one() ); if( parent && (toplevel = iwindow_get_root( parent )) && IS_IWINDOW( toplevel ) && IWINDOW( toplevel )->infobar ) infobar_set( IWINDOW( toplevel )->infobar, type, error_get_top(), "%s", error_get_sub() ); else switch( type ) { case GTK_MESSAGE_INFO: box_info( parent, error_get_top(), "%s", error_get_sub() ); break; case GTK_MESSAGE_ERROR: box_alert( parent ); break; default: break; } } ================================================ FILE: src/iwindow.h ================================================ /* make and manage windows ... subclass off this for dialog boxes */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #ifndef IWINDOW_H #define IWINDOW_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define TYPE_IWINDOW (iwindow_get_type()) #define IWINDOW( obj ) (GTK_CHECK_CAST( (obj), TYPE_IWINDOW, iWindow )) #define IWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_IWINDOW, iWindowClass )) #define IS_IWINDOW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_IWINDOW )) #define IS_IWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_IWINDOW )) #define IWINDOW_GET_CLASS( obj ) \ (GTK_CHECK_GET_CLASS( (obj), TYPE_IWINDOW, iWindowClass )) typedef struct _iWindow iWindow; /* Our cursor shapes. */ typedef enum _iWindowShape { /* Tool shapes. */ IWINDOW_SHAPE_DROPPER = 0, IWINDOW_SHAPE_PEN, IWINDOW_SHAPE_SMUDGE, IWINDOW_SHAPE_SMEAR, IWINDOW_SHAPE_TEXT, IWINDOW_SHAPE_RECT, IWINDOW_SHAPE_FLOOD, IWINDOW_SHAPE_MOVE, IWINDOW_SHAPE_EDIT, IWINDOW_SHAPE_MAGIN, IWINDOW_SHAPE_MAGOUT, /* Resize shapes. */ IWINDOW_SHAPE_TOP, IWINDOW_SHAPE_BOTTOM, IWINDOW_SHAPE_LEFT, IWINDOW_SHAPE_RIGHT, IWINDOW_SHAPE_TOPRIGHT, IWINDOW_SHAPE_TOPLEFT, IWINDOW_SHAPE_BOTTOMRIGHT, IWINDOW_SHAPE_BOTTOMLEFT, /* Watch positions. */ IWINDOW_SHAPE_HGLASS1, IWINDOW_SHAPE_HGLASS2, IWINDOW_SHAPE_HGLASS3, IWINDOW_SHAPE_HGLASS4, IWINDOW_SHAPE_HGLASS5, IWINDOW_SHAPE_HGLASS6, IWINDOW_SHAPE_HGLASS7, IWINDOW_SHAPE_HGLASS8, /* No shape set (shape we inherit). */ IWINDOW_SHAPE_NONE, IWINDOW_SHAPE_LAST } iWindowShape; /* Keep a set of these, one for each of the clients who might want to set the * shape for a window. */ typedef struct { iWindow *iwnd; int priority; /* Higher priority == more on top */ const char *name; /* For debugging */ /* Shape currently requested by this user. */ iWindowShape shape; } iWindowCursorContext; /* The result from a window/dialog/whatever ... not just a bool. */ typedef enum iwindow_result { IWINDOW_ERROR = 0, /* Tried but failed */ IWINDOW_YES, /* User tried the action */ IWINDOW_NO /* User cancelled */ } iWindowResult; /* Our callbacks don't return iWindowResult, instead * they are given a notify function (plus an environment parameter) which * they use to inform their caller of their iWindowResult. */ typedef void (*iWindowNotifyFn)( void *, iWindowResult ); /* What our callbacks look like. */ typedef void (*iWindowFn)( iWindow *, void *, iWindowNotifyFn, void * ); /* Build function for window contents. */ typedef void (*iWindowBuildFn)( iWindow *, GtkWidget *, void *, void *, void * ); /* A suspension ... an iWindowFn plus a set of args we are saving for later. */ typedef struct { iWindowFn fn; iWindow *iwnd; void *client; iWindowNotifyFn nfn; void *sys; } iWindowSusp; #define IWINDOW_SUSP( X ) ((iWindowSusp *) (X)) struct _iWindow { GtkWindow parent_object; /* Parent window. Used for (eg.) image displays windows which we need * to float over the main workspace. */ GtkWidget *parent; /* Our parent widget */ iWindow *parent_window; /* Our parent window */ guint parent_unmap_sid; /* Watch parent death here */ GtkWidget *work; GtkAccelGroup *accel_group; Infobar *infobar; char *title; /* Action stuff. We init this and add a few common actions to help out * subclasses. */ GtkActionGroup *action_group; GtkUIManager *ui_manager; /* Per instance build function. */ iWindowBuildFn build; void *build_a, *build_b, *build_c; /* Called before cancellable popdown ... _TRUE from this will * destroy window, _FALSE won't, _ERROR won't and pops an error box. */ iWindowFn popdown; void *popdown_a; /* Notify handling. */ gboolean destroy; /* True if being destroyed */ int pending; /* Number of notifies waiting on */ /* Cursor handling. */ iWindowShape shape; /* Global shape ... for hglass */ GSList *contexts; /* Set of other requested shapes */ GdkWindow *work_window; /* The window we actually set */ /* Size memorization. */ const char *width_pref; /* Prefs we save width/height in */ const char *height_pref; }; typedef struct _iWindowClass { GtkWindowClass parent_class; /* Per class build/popdown functions. */ void (*build)( GtkWidget * ); void (*popdown)( GtkWidget * ); /* Whether windows of this class should be marked as transient for * their parents (eg. dialogs usually are). */ gboolean transient; } iWindowClass; int iwindow_number( void ); iWindow *iwindow_pick_one( void ); typedef void (*iWindowMapFn)( iWindow *, void * ); void *iwindow_map_all( iWindowMapFn fn, void *a ); iWindowCursorContext *iwindow_cursor_context_new( iWindow *, int, const char * ); void iwindow_cursor_context_set_cursor( iWindowCursorContext *, iWindowShape ); void iwindow_cursor_context_destroy( iWindowCursorContext *cntxt ); iWindowSusp *iwindow_susp_new( iWindowFn, iWindow *, void *, iWindowNotifyFn, void * ); void iwindow_susp_trigger( iWindowSusp * ); void iwindow_susp_return( void *, iWindowResult ); void iwindow_susp_comp( void *, iWindowResult ); void iwindow_notify_send( iWindow *iwnd, iWindowFn fn, void *client, iWindowNotifyFn back, void *sys ); void iwindow_notify_return( iWindow *iwnd ); void iwindow_true_cb( iWindow *, void *, iWindowNotifyFn nfn, void *sys ); void iwindow_false_cb( iWindow *, void *, iWindowNotifyFn nfn, void *sys ); void iwindow_notify_null( void *client, iWindowResult result ); GtkType iwindow_get_type( void ); GtkWidget *iwindow_new( GtkWindowType ); void iwindow_set_title( iWindow *, const char *, ... ) __attribute__((format(printf, 2, 3))); void iwindow_set_build( iWindow *, iWindowBuildFn, void *, void *, void * ); void iwindow_set_popdown( iWindow *, iWindowFn, void * ); void iwindow_set_size_prefs( iWindow *, const char *, const char * ); void iwindow_set_work_window( iWindow *iwnd, GdkWindow *work_window ); void iwindow_set_parent( iWindow *, GtkWidget *par ); void iwindow_build( iWindow * ); void *iwindow_kill( iWindow * ); void iwindow_kill_action_cb( GtkAction *action, iWindow *iwnd ); GtkWidget *iwindow_get_root( GtkWidget *widget ); GtkWidget *iwindow_get_root_noparent( GtkWidget *widget ); void iwindow_alert( GtkWidget *parent, GtkMessageType type ); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* IWINDOW_H */ ================================================ FILE: src/lex.l ================================================ %{ /* Lexer for image processing expressions. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" #ifdef HAVE_FLEX /* Flex has a different input mechanism :( */ #define YY_INPUT(buf,result,max_size) { \ extern int ip_input( void ); \ int c = ip_input(); \ result = (c == 0) ? YY_NULL : (buf[0] = c, 1); \ } #undef unput #define unput ip_unput #else /*HAVE_FLEX*/ /* Assume this is plain lex. */ /* Redefine input, output, unput and yywrap. */ #undef input #undef output #undef unput #undef YYLMAX /* See parse.y for input and unput. */ #define output(A) (error( "output called by lex" )) #define YYLMAX MAX_STRSIZE #define unput ip_unput #define input ip_input #endif /*HAVE_FLEX*/ /* Stuff from bison. */ #include "parse.h" /* Read a string into a buffer. Read up to the " character, " can be * escaped with '\'. */ static void read_string( char *buf ) { int ch; int i; /* Read up to \n, ", EOF, ignoring \" * Don't forget about "\\" though. */ for( i = 0; (ch = ip_input()); i++ ) { if( ch == EOF || ch == '\n' || ch == '"' || ch == '\0' ) break; if( i >= MAX_STRSIZE ) yyerror( _( "line too long" ) ); buf[i] = ch; if( ch == '\\' ) { ch = ip_input(); if( ch == EOF || ch == '\n' || ch == '\0' ) break; if( i >= MAX_STRSIZE ) yyerror( _( "line too long" ) ); buf[++i] = ch; } } buf[i] = '\0'; if( ch == '\n' ) yyerror( _( "end of line inside string" ) ); if( ch == EOF || ch == '\0' ) yyerror( _( "no end of string" ) ); } /* Read a char constant. The leading ' has already been seen. Cases to consider: * '\n' * '\\' * ''' (illegal in C, but I think we allow it) * '\'' */ static int read_char( void ) { int ch; ch = ip_input(); if( ch == EOF || ch == '\n' || ch == '\0' ) yyerror( _( "bad char constant" ) ); if( ch == '\\' ) { char buf[3]; char buf2[3]; buf[0] = ch; buf[1] = ch = ip_input(); buf[2] = '\0'; if( ch == EOF || ch == '\n' || ch == '\0' ) yyerror( _( "bad char constant" ) ); my_strccpy( buf2, buf ); ch = buf2[0]; } if( '\'' != ip_input() ) yyerror( _( "bad char constant" ) ); return( ch ); } %} %Start DOT %Start BINARY %option noyywrap %% \/\* { int ch; while( (ch = input()) != EOF ) if( ch == '*' ) { if( (ch = input()) == '/' ) break; else unput( ch ); } else if( ch == '/' ) { if( (ch = input()) == '*' ) yyerror( _( "nested comment" ) ); else unput( ch ); } if( ch == EOF ) yyerror( _( "no end of comment" ) ); } # | (\/\/) { int ch; /* Read string up to \n, EOF. */ while( (ch = input()) != EOF && ch != '\n' && ch != '\0') ; } \#separator { BEGIN 0; return( TK_SEPARATOR ); } \#dialog { BEGIN 0; return( TK_DIALOG ); } class { BEGIN 0; return( TK_CLASS ); } scope { BEGIN 0; return( TK_SCOPE ); } char { BEGIN 0; return( TK_CHAR ); } short { BEGIN 0; return( TK_SHORT ); } int { BEGIN 0; return( TK_INT ); } float { BEGIN 0; return( TK_FLOAT ); } double { BEGIN 0; return( TK_DOUBLE ); } signed { BEGIN 0; return( TK_SIGNED ); } unsigned { BEGIN 0; return( TK_UNSIGNED ); } complex { BEGIN 0; return( TK_COMPLEX ); } if { BEGIN 0; return( TK_IF ); } then { BEGIN 0; return( TK_THEN ); } else { BEGIN 0; return( TK_ELSE ); } \.\.\. { BEGIN 0; return( TK_DOTDOTDOT ); } \.\. { BEGIN 0; return( TK_DOTDOTDOT ); } true | TRUE { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_BOOL; yylval.yy_const.val.bol = TRUE; return( TK_CONST ); } false | FALSE { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_BOOL; yylval.yy_const.val.bol = FALSE; return( TK_CONST ); } [a-zA-Z_][a-zA-Z0-9_']* { BEGIN BINARY; yylval.yy_name = im_strdupn( yytext ); return( TK_TAG ); } [a-zA-Z_][a-zA-Z0-9_']* { char *name = model_loadstate_rewrite_name( yytext ); BEGIN BINARY; if( name ) { yylval.yy_name = im_strdupn( name ); vips_buf_change( &lex_text, yytext, name ); } else yylval.yy_name = im_strdupn( yytext ); return( TK_IDENT ); } \$[a-zA-Z_][a-zA-Z0-9_']* { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_STR; yylval.yy_const.val.str = im_strdupn( yytext + 1 ); return( TK_CONST ); } \( { BEGIN 0; return( '(' ); } \) { BEGIN BINARY; return( ')' ); } \+\+ { BEGIN 0; return( TK_JOIN ); } \-\- { BEGIN 0; return( TK_DIFF ); } \+ | \- { BEGIN 0; return( *yytext ); } \- { BEGIN 0; return( TK_UMINUS ); } \+ { BEGIN 0; return( TK_UPLUS ); } \< { BEGIN 0; return( TK_LESS ); } \<\= { BEGIN 0; return( TK_LESSEQ ); } \> { BEGIN 0; return( TK_MORE ); } \>\= { BEGIN 0; return( TK_MOREEQ ); } \=\> { BEGIN 0; return( TK_TO ); } \& { BEGIN 0; return( TK_BAND ); } \&\& { BEGIN 0; return( TK_LAND ); } \:\: { BEGIN 0; return( TK_SUCHTHAT ); } \*\* { BEGIN 0; return( TK_POW ); } \>\> { BEGIN 0; return( TK_RSHIFT ); } \<\< { BEGIN 0; return( TK_LSHIFT ); } \<\- { BEGIN 0; return( TK_FROM ); } \| { BEGIN 0; return( TK_BOR ); } \|\| { BEGIN 0; return( TK_LOR ); } \=\= { BEGIN 0; return( TK_EQ ); } \=\=\= { BEGIN 0; return( TK_PEQ ); } \!\= { BEGIN 0; return( TK_NOTEQ ); } \!\=\= { BEGIN 0; return( TK_PNOTEQ ); } \\ { BEGIN 0; return( TK_LAMBDA ); } \^ | \? | \* | \/ | \% | \, | \! | \; | \[ | \: | \= | \~ | \@ | \{ | \} { BEGIN 0; return( *yytext ); } \. { BEGIN DOT; return( *yytext ); } \] { BEGIN BINARY; return( *yytext ); } 0x[0-9a-fA-F]+ { unsigned int i; BEGIN BINARY; if( sscanf( yytext, "0x%x", &i ) != 1 ) nip2yyerror( _( "bad number %s" ), yytext ); yylval.yy_const.type = PARSE_CONST_NUM; yylval.yy_const.val.num = i; return( TK_CONST ); } [0-9]*(\.[0-9]+)?([eE][+-]?[0-9]+)?[ij]? { double d; int ch; BEGIN BINARY; d = g_ascii_strtod( yytext, NULL ); yylval.yy_const.type = PARSE_CONST_NUM; yylval.yy_const.val.num = d; ch = yytext[strlen( yytext ) - 1]; if( ch == 'i' || ch == 'j' ) yylval.yy_const.type = PARSE_CONST_COMPLEX; return( TK_CONST ); } \' { BEGIN BINARY; yylval.yy_const.type = PARSE_CONST_CHAR; yylval.yy_const.val.ch = read_char(); return( TK_CONST ); } \" { ModelLoadState *state = model_loadstate; char buf[MAX_STRSIZE]; char buf2[MAX_STRSIZE]; BEGIN BINARY; read_string( buf ); /* We need to keep buf as exactly the string in the source, * including before interpretation of \ escapes, for the * vips_buf_change() to work. */ my_strccpy( buf2, buf ); if( state && state->rewrite_path ) { char buf3[FILENAME_MAX]; path_compact( buf2 ); /* We've interpreted \n etc. in my_strccpy() above, plus we * have nativised paths from / to \ and therefore introduced * backslashes that weren't there before. * * Before we write source code out again, we must reescape * everything. */ my_strecpy( buf3, buf2, TRUE ); vips_buf_change( &lex_text, buf, buf3 ); } if( strcmp( buf2, "" ) == 0 ) yylval.yy_const.type = PARSE_CONST_ELIST; else { yylval.yy_const.type = PARSE_CONST_STR; yylval.yy_const.val.str = im_strdupn( buf2 ); } return( TK_CONST ); } [ \t\n\r\m\01] ; . { nip2yyerror( _( "illegal character \"%c\"" ), *yytext ); } ================================================ FILE: src/link.c ================================================ /* Links between top-level syms and the exprs which reference them */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG #define DEBUG_DIRTY */ #include "ip.h" void * link_expr_destroy( LinkExpr *le ) { GSList **llinks = le->dynamic ? &le->link->dynamic_links : &le->link->static_links; GSList **elinks = le->dynamic ? &le->expr->dynamic_links : &le->expr->static_links; #ifdef DEBUG printf( "link_expr_destroy: removing expr " ); symbol_name_print( le->expr->sym ); printf( "referencing link->child = " ); symbol_name_print( le->link->child ); printf( "\n" ); #endif /*DEBUG*/ *llinks = slist_remove_all( *llinks, le ); *elinks = slist_remove_all( *elinks, le ); im_free( le ); return( NULL ); } static LinkExpr * link_expr_new( Link *link, Expr *expr, gboolean dynamic ) { GSList **llinks = dynamic ? &link->dynamic_links : &link->static_links; GSList **elinks = dynamic ? &expr->dynamic_links : &expr->static_links; LinkExpr *le; g_assert( expr_get_root_dynamic( expr )->sym == link->parent ); #ifdef DEBUG printf( "link_expr_new: expr " ); symbol_name_print( expr->sym ); printf( "references link->child = " ); symbol_name_print( link->child ); printf( "\n" ); #endif /*DEBUG*/ if( !(le = INEW( NULL, LinkExpr )) ) return( NULL ); le->link = link; le->expr = expr; le->count = 1; le->dynamic = dynamic; *llinks = g_slist_prepend( *llinks, le ); *elinks = g_slist_prepend( *elinks, le ); return( le ); } /* Make a new serial number. */ int link_serial_new( void ) { static int serial = 0; return( serial++ ); } /* Fwd ref. */ static void *symbol_dirty_set( Symbol *sym ); /* child has become dirty ... update parent's dirty count. */ static void * link_dirty_child( Link *link ) { g_assert( link->parent->ndirtychildren >= 0 ); link->parent->ndirtychildren += 1; if( link->parent->ndirtychildren == 1 ) /* Parent had no dirty children ... it does now. */ symbol_dirty_set( link->parent ); symbol_state_change( link->parent ); return( NULL ); } /* link->parent no longer has link->child as a dirty child (cleaned or * removed) ... update counts. */ static void * link_clean_child( Link *link ) { Symbol *parent = link->parent; /* One fewer dirty children! */ parent->ndirtychildren--; g_assert( parent->ndirtychildren >= 0 ); /* Have we just cleaned the last dirty child of link->parent? If we * have and if link->parent has an error, clear the error so that * link->parent gets a chance to recalc. The new value of * link->child might fix the problem. */ if( parent->ndirtychildren == 0 ) expr_error_clear( parent->expr ); symbol_state_change( parent ); return( NULL ); } /* Junk a link. */ void * link_destroy( Link *link ) { #ifdef DEBUG printf( "link_destroy: destroying link from " ); symbol_name_print( link->parent ); printf( "to " ); symbol_name_print( link->child ); printf( "\n" ); #endif /*DEBUG*/ if( link->child->dirty ) (void) link_clean_child( link ); link->parent->topchildren = slist_remove_all( link->parent->topchildren, link ); link->child->topparents = slist_remove_all( link->child->topparents, link ); slist_map( link->static_links, (SListMapFn) link_expr_destroy, NULL ); slist_map( link->dynamic_links, (SListMapFn) link_expr_destroy, NULL ); im_free( link ); return( NULL ); } /* Make a new link. */ static Link * link_new( Symbol *child, Symbol *parent ) { Link *link; g_assert( is_top( parent ) && is_top( child ) ); g_assert( parent != child ); #ifdef DEBUG printf( "link_new: making link from " ); symbol_name_print( parent ); printf( "to " ); symbol_name_print( child ); printf( "\n" ); #endif /*DEBUG*/ if( !(link = INEW( NULL, Link )) ) return( NULL ); link->parent = parent; link->child = child; link->serial = 0; link->static_links = NULL; link->dynamic_links = NULL; parent->topchildren = g_slist_prepend( parent->topchildren, link ); child->topparents = g_slist_prepend( child->topparents, link ); /* If the new child is dirty, note it. */ if( child->dirty ) link_dirty_child( link ); return( link ); } static Link * link_find_child_sub( Link *link, Symbol *child ) { if( link->child == child ) return( link ); return( NULL ); } /* Look up connection between child and parent. */ static Link * link_find_child( Symbol *child, Symbol *parent ) { return( (Link *) slist_map( parent->topchildren, (SListMapFn) link_find_child_sub, child ) ); } static void * link_expr_find_expr_sub( LinkExpr *le, Expr *expr ) { if( le->expr == expr ) return( le ); return( NULL ); } /* Look up a linkexpr by expr. */ static LinkExpr * link_expr_find_expr( Link *link, Expr *expr, gboolean dynamic ) { GSList *links = dynamic ? link->dynamic_links : link->static_links; return( (LinkExpr *) slist_map( links, (SListMapFn) link_expr_find_expr_sub, expr ) ); } /* Add a reference from expr to child to the link graph. */ void * link_add( Symbol *child, Expr *expr, gboolean dynamic ) { Expr *parent = expr_get_root_dynamic( expr ); Link *link; LinkExpr *le; #ifdef DEBUG printf( "link_add: child = " ); symbol_name_print( child ); printf( "; expr = " ); expr_name_print( expr ); printf( "; dynamic = %s\n", bool_to_char( dynamic ) ); #endif /*DEBUG*/ g_assert( parent ); g_assert( parent->sym ); g_assert( is_top( child ) && is_top( parent->sym ) ); g_assert( child != parent->sym ); if( !(link = link_find_child( child, parent->sym )) ) { if( !(link = link_new( child, parent->sym )) ) return( child ); } if( !(le = link_expr_find_expr( link, expr, dynamic )) ) { if( !(le = link_expr_new( link, expr, dynamic )) ) return( child ); } else le->count++; return( NULL ); } /* Remove a ref from expr to child. */ void * link_remove( Symbol *child, Expr *expr, gboolean dynamic ) { Symbol *parent = expr_get_root_dynamic( expr )->sym; Link *link = link_find_child( child, parent ); LinkExpr *le = link_expr_find_expr( link, expr, dynamic ); g_assert( is_top( parent ) && is_top( child ) ); g_assert( parent != child ); g_assert( link ); le->count--; if( le->count == 0 ) { if( link_expr_destroy( le ) ) return( child ); } if( !link->static_links && !link->dynamic_links ) { if( link_destroy( link ) ) return( child ); } return( NULL ); } /* Is this a ref to a top-level? Add to link graph if it is. */ static void * link_children_expr_sub( Symbol *child, Expr *expr ) { if( is_top( child ) ) { Expr *root = expr_get_root_dynamic( expr ); /* Don't need to record recursive refs. */ if( root && root->sym && root->sym != child ) { if( link_add( child, expr, FALSE ) ) return( child ); } } return( NULL ); } /* Fwd. */ static void *link_children( Symbol *child, Symbol *parent ); /* Add any refs to top-level syms within this local to the * top-level sym we are within. */ static void * link_children_expr( Expr *expr, Symbol *parent ) { if( expr->compile ) { Compile *compile = expr->compile; /* Add refs which local makes directly. */ if( slist_map( compile->children, (SListMapFn) link_children_expr_sub, expr ) ) return( expr ); /* ... and recurse for sub-children. */ (void) icontainer_map( ICONTAINER( compile ), (icontainer_map_fn) link_children, parent, NULL ); } return( NULL ); } /* Add any refs to top-level syms within this local to the * top-level sym we are within. */ static void * link_children( Symbol *child, Symbol *parent ) { if( child->expr ) { if( link_children_expr( child->expr, parent ) ) return( child ); } return( NULL ); } /* row is editing sym's value ... add any dependancies the user has included * there. */ static void * link_row( Model *model, Symbol *parent ) { if( !IS_ROW( model ) || !ROW( model )->expr ) return( NULL ); /* Add any stuff in this row. */ return( link_children_expr( ROW( model )->expr, parent ) ); } static void * symbol_ndirty_sub( Link *link, int *nd ) { if( link->child->dirty ) *nd += 1; return( NULL ); } /* Count the number of dirty children. Used to generate initial leaf counts * and for assert() checking. */ int symbol_ndirty( Symbol *sym ) { int nd = 0; (void) slist_map( sym->topchildren, (SListMapFn) symbol_ndirty_sub, &nd ); return( nd ); } /* Fix a leaf count. */ void * symbol_fix_counts( Symbol *sym ) { #ifdef DEBUG int old_count = sym->ndirtychildren; #endif /*DEBUG*/ sym->ndirtychildren = symbol_ndirty( sym ); #ifdef DEBUG g_assert( sym->ndirtychildren == old_count ); #endif /*DEBUG*/ symbol_state_change( sym ); return( NULL ); } /* Junk all old links, static + dynamic. */ void symbol_link_destroy( Symbol *sym ) { (void) slist_map( sym->topchildren, (SListMapFn) link_destroy, NULL ); } /* Scan a symbol, remaking all the links. */ void symbol_link_build( Symbol *sym ) { g_assert( is_top( sym ) ); /* Make static links for our expr and all subexprs. If this symbol * is being edited, get stuff from the edited value. */ if( sym->expr ) { if( sym->expr->row ) (void) icontainer_map_all( ICONTAINER( sym->expr->row ), (icontainer_map_fn) link_row, sym ); else (void) link_children_expr( sym->expr, sym ); } #ifdef DEBUG printf( "symbol_link_build: " ); symbol_name_print( sym ); printf( "\n" ); dump_links( sym ); #endif /*DEBUG*/ } static void * link_dirty_set_sub( LinkExpr *le, int serial ) { return( expr_dirty( le->expr, serial ) ); } /* Mark exprs in parent dirty. These may be sub exprs, so parent is not * necessarily going to be symbol_dirty_set() ... eg. A2 may be displaying an * instance of class "fred", and we might have edited one of A2's members to * refer to A1 ... but A2 depends on A1, fred does not. */ static void * link_dirty_set( Link *link, int serial ) { /* Mark exprs in parent dirty. */ if( slist_map( link->static_links, (SListMapFn) link_dirty_set_sub, GINT_TO_POINTER( serial ) ) || slist_map( link->dynamic_links, (SListMapFn) link_dirty_set_sub, GINT_TO_POINTER( serial ) ) ) return( link ); return( NULL ); } /* Walk the link graph, marking stuff for recomputation ... link->child has * changed, mark link->parent dirty. */ static void * link_dirty_walk( Link *link, int serial ) { /* Have we walked down this link before? */ if( link->serial == serial ) return( NULL ); link->serial = serial; /* Mark all exprs in parent dirty. */ return( link_dirty_set( link, serial ) ); } /* A symbol has changed ... walk the link graph, marking stuff dirty as * required. We don't mark this sym dirty. */ void * symbol_dirty_intrans( Symbol *sym, int serial ) { g_assert( is_top( sym ) ); return( slist_map( sym->topparents, (SListMapFn) link_dirty_walk, GINT_TO_POINTER( serial ) ) ); } static void * symbol_dirty_set( Symbol *sym ) { g_assert( is_top( sym ) ); /* Clear error, to make sure we will recomp it. */ if( sym->expr ) expr_error_clear( sym->expr ); if( !sym->dirty ) { #ifdef DEBUG_DIRTY printf( "symbol_dirty_set: " ); symbol_name_print( sym ); printf( "(%p)\n", sym ); #endif /*DEBUG_DIRTY*/ /* Change of state. */ sym->dirty = TRUE; /* Update dirty counts on our parents. */ (void) slist_map( sym->topparents, (SListMapFn) link_dirty_child, NULL ); /* Note change in leaf set and display. */ symbol_state_change( sym ); } return( NULL ); } /* ... mark this one as well. */ void * symbol_dirty( Symbol *sym, int serial ) { g_assert( is_top( sym ) ); symbol_dirty_set( sym ); return( symbol_dirty_intrans( sym, serial ) ); } void * link_dirty_total( Link *link, int serial ) { static int recursion_depth = 0; /* Entering: note new recursion. */ if( recursion_depth++ > 1000 ) { error_top( _( "Circular dependency." ) ); error_sub( _( "Circular dependency detected near " "symbol \"%s\"." ), IOBJECT( link->parent )->name ); recursion_depth = 0; return( link ); } /* Mark this sub-tree as dirty. */ symbol_dirty( link->child, serial ); /* ... and repeat for any parents. */ if( link->child->type != SYM_ZOMBIE ) if( slist_map( link->child->topchildren, (SListMapFn) link_dirty_total, GINT_TO_POINTER( serial ) ) ) return( link ); /* Pop recursion measure. */ recursion_depth--; return( NULL ); } /* As above, but mark children as dirty as well. Used by force recalc to make * sure that everything is completely rebuilt. Be careful of cycles! */ void * symbol_dirty_total( Symbol *sym, int serial ) { if( sym->type == SYM_ZOMBIE ) return( NULL ); /* No children: just mark this sub-tree as dirty. */ if( !sym->topchildren && symbol_dirty( sym, serial ) ) return( sym ); if( slist_map( sym->topchildren, (SListMapFn) link_dirty_total, GINT_TO_POINTER( serial ) ) ) return( sym ); return( NULL ); } /* Mark a symbol as clean. Knock down the leaf count of the things which refer * to us ... one of them may turn into a leaf as a result. */ void * symbol_dirty_clear( Symbol *sym ) { g_assert( is_top( sym ) ); if( sym->dirty ) { #ifdef DEBUG_DIRTY printf( "symbol_dirty_clear: " ); symbol_name_print( sym ); printf( "(%p)\n", sym ); #endif /*DEBUG_DIRTY*/ /* Change of state. */ sym->dirty = FALSE; symbol_state_change( sym ); /* Update dirty counts on our parents. */ (void) slist_map( sym->topparents, (SListMapFn) link_clean_child, NULL ); } return( NULL ); } ================================================ FILE: src/link.h ================================================ /* Links between top-level syms and the exprs which reference them */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* A sub-link ... the expr in parent that actually references child, plus * the number of times it makes the reference. */ struct _LinkExpr { Link *link; /* Link we are part of */ Expr *expr; /* Expr that references child */ int count; /* Number of times expr references child */ gboolean dynamic; /* True for dynamic link */ }; /* A link object! */ struct _Link { Symbol *parent; /* This top-level symbol contains exprs ... */ Symbol *child; /* ... which reference this symbol */ /* Link serial number ... when we walk the symbol graph marking * stuff dirty, use this to stop repeat trips along links, and * avoid getting stuck in cycles. */ int serial; /* The expressions inside parent which contain direct references to * child. If parent is in the tally, there can be lots of these. * * Two sort of links: static links, which we can deduce from * compile-time analysis of the expr and which only change when * the user edits and we recompile, and dynamic links which we * clear when we regenerate the heap image of the function, and add * to during evaluation. */ GSList *static_links; GSList *dynamic_links; }; void *link_expr_destroy( LinkExpr *le ); void *link_destroy( Link *link ); void *link_add( Symbol *child, Expr *expr, gboolean dynamic ); void *link_remove( Symbol *child, Expr *expr, gboolean dynamic ); int symbol_ndirty( Symbol *sym ); void *symbol_fix_counts( Symbol *sym ); void symbol_link_destroy( Symbol *sym ); void symbol_link_build( Symbol *sym ); int link_serial_new( void ); void *symbol_dirty_intrans( Symbol *sym, int serial ); void *symbol_dirty( Symbol *sym, int serial ); void *symbol_dirty_total( Symbol *sym, int serial ); void *symbol_dirty_clear( Symbol *sym ); ================================================ FILE: src/log ================================================ workspacegroupview_switch_page_cb: 0 moving tab view_model_child_remove: child Workspace "test"; parent Workspacegroup "(null)" view_model_child_remove: parent_view = view of Workspacegroup "(null)" view_viewchild_test_child_model: model Workspace "test" view_viewchild_destroy: view Workspacegroupview watching model Workspace workspacegroupview_switch_page_cb: 0 about to add view_model_child_add: parent Workspacegroup "tab1" view_viewchild_test_child_model: model Workspace "test" view_viewchild_new: view "Workspacegroupview" watching Workspace "test" view_viewchild_changed: Workspace "test", adding view view_real_link: linking Workspaceview to model Workspace "test" view_real_child_add: parent Workspacegroupview, child Workspaceview view_viewchild_test_child_model: model Workspace "test" view_viewchild_test_child_model: model Workspace "test" view_viewchild_new: view "Workspaceview" watching Column "B" view_viewchild_changed: Column "B", adding view view_real_link: linking Columnview to model Column "B" view_real_child_add: parent Workspaceview, child Columnview view_viewchild_test_child_model: model Column "B" view_viewchild_new: view "Columnview" watching Subcolumn "(null)" view_viewchild_changed: Subcolumn "(null)", adding view view_real_link: linking Subcolumnview to model Subcolumn "(null)" view_real_child_add: parent Columnview, child Subcolumnview view_viewchild_test_child_model: model Subcolumn "(null)" view_viewchild_new: view "Subcolumnview" watching Row "B1" view_viewchild_changed: Row "B1", adding view view_real_link: linking Rowview to model Row "B1" view_real_child_add: parent Subcolumnview, child Rowview view_viewchild_test_child_model: model Row "B1" view_viewchild_new: view "Rowview" watching Rhs "(null)" view_viewchild_changed: Rhs "(null)", adding view view_real_link: linking Rhsview to model Rhs "(null)" view_real_child_add: parent Rowview, child Rhsview view_viewchild_test_child_model: model Rhs "(null)" view_viewchild_new: view "Rhsview" watching iImage "Image" view_viewchild_changed: iImage "Image", adding view view_real_link: linking iImageview to model iImage "Image" view_real_child_add: parent Rhsview, child iImageview view_viewchild_test_child_model: model iImage "Image" view_viewchild_new: view "Rhsview" watching Subcolumn "(null)" view_viewchild_changed: Subcolumn "(null)", adding view view_real_link: linking Subcolumnview to model Subcolumn "(null)" view_real_child_add: parent Rhsview, child Subcolumnview view_viewchild_test_child_model: model Subcolumn "(null)" view_viewchild_test_child_model: model Subcolumn "(null)" view_viewchild_new: view "Rhsview" watching iText "(null)" view_viewchild_changed: iText "(null)", adding view view_real_link: linking iTextview to model iText "(null)" view_real_child_add: parent Rhsview, child iTextview view_viewchild_test_child_model: model iText "(null)" view_viewchild_test_child_model: model iText "(null)" view_viewchild_test_child_model: model iText "(null)" view_viewchild_new: view "Workspaceview" watching Column "A" view_viewchild_changed: Column "A", adding view view_real_link: linking Columnview to model Column "A" view_real_child_add: parent Workspaceview, child Columnview view_viewchild_test_child_model: model Column "A" view_viewchild_test_child_model: model Column "A" view_viewchild_new: view "Columnview" watching Subcolumn "(null)" view_viewchild_changed: Subcolumn "(null)", adding view view_real_link: linking Subcolumnview to model Subcolumn "(null)" view_real_child_add: parent Columnview, child Subcolumnview view_viewchild_test_child_model: model Subcolumn "(null)" view_viewchild_new: view "Subcolumnview" watching Row "A1" view_viewchild_changed: Row "A1", adding view view_real_link: linking Rowview to model Row "A1" view_real_child_add: parent Subcolumnview, child Rowview view_viewchild_test_child_model: model Row "A1" view_viewchild_new: view "Rowview" watching Rhs "(null)" view_viewchild_changed: Rhs "(null)", adding view view_real_link: linking Rhsview to model Rhs "(null)" view_real_child_add: parent Rowview, child Rhsview view_viewchild_test_child_model: model Rhs "(null)" view_viewchild_new: view "Rhsview" watching iText "(null)" view_viewchild_changed: iText "(null)", adding view view_real_link: linking iTextview to model iText "(null)" view_real_child_add: parent Rhsview, child iTextview view_viewchild_test_child_model: model iText "(null)" view_viewchild_new: view "Subcolumnview" watching Row "A2" view_viewchild_changed: Row "A2", adding view view_real_link: linking Rowview to model Row "A2" view_real_child_add: parent Subcolumnview, child Rowview view_viewchild_test_child_model: model Row "A2" view_viewchild_test_child_model: model Row "A2" view_viewchild_new: view "Rowview" watching Rhs "(null)" view_viewchild_changed: Rhs "(null)", adding view view_real_link: linking Rhsview to model Rhs "(null)" view_real_child_add: parent Rowview, child Rhsview view_viewchild_test_child_model: model Rhs "(null)" view_viewchild_new: view "Rhsview" watching iText "(null)" view_viewchild_changed: iText "(null)", adding view view_real_link: linking iTextview to model iText "(null)" view_real_child_add: parent Rhsview, child iTextview view_viewchild_test_child_model: model iText "(null)" view_viewchild_new: view "Subcolumnview" watching Row "A3" view_viewchild_changed: Row "A3", adding view view_real_link: linking Rowview to model Row "A3" view_real_child_add: parent Subcolumnview, child Rowview view_viewchild_test_child_model: model Row "A3" view_viewchild_test_child_model: model Row "A3" view_viewchild_test_child_model: model Row "A3" view_viewchild_new: view "Rowview" watching Rhs "(null)" view_viewchild_changed: Rhs "(null)", adding view view_real_link: linking Rhsview to model Rhs "(null)" view_real_child_add: parent Rowview, child Rhsview view_viewchild_test_child_model: model Rhs "(null)" view_viewchild_new: view "Rhsview" watching iText "(null)" view_viewchild_changed: iText "(null)", adding view view_real_link: linking iTextview to model iText "(null)" view_real_child_add: parent Rhsview, child iTextview view_viewchild_test_child_model: model iText "(null)" workspacegroupview_switch_page_cb: 0 tab move done view_model_front: test ================================================ FILE: src/log.c ================================================ /* Abstract base class for a log window: errors, link report, log, etc. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Send log to stdout as well #define DEBUG_FILE */ #include "ip.h" static iWindowClass *parent_class = NULL; static void log_build( GtkWidget *widget ) { Log *log = LOG( widget ); iWindow *iwnd = IWINDOW( widget ); LogClass *log_class = LOG_GET_CLASS( log ); GError *error; GtkWidget *mbar; GtkWidget *swin; PangoFontDescription *font_desc; IWINDOW_CLASS( parent_class )->build( widget ); gtk_action_group_add_actions( iwnd->action_group, log_class->actions, log_class->n_actions, GTK_WINDOW( log ) ); gtk_action_group_add_toggle_actions( iwnd->action_group, log_class->toggle_actions, log_class->n_toggle_actions, GTK_WINDOW( log ) ); if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager, log_class->ui_description, -1, &error ) ) { g_message( "building menus failed: %s", error->message ); g_error_free( error ); exit( EXIT_FAILURE ); } mbar = gtk_ui_manager_get_widget( iwnd->ui_manager, log_class->menu_bar_name ); gtk_box_pack_start( GTK_BOX( iwnd->work ), mbar, FALSE, FALSE, 0 ); gtk_widget_show( mbar ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_start( GTK_BOX( iwnd->work ), swin, TRUE, TRUE, 0 ); gtk_widget_show( swin ); log->view = gtk_text_view_new(); gtk_text_view_set_editable( GTK_TEXT_VIEW( log->view ), FALSE ); gtk_text_view_set_cursor_visible( GTK_TEXT_VIEW( log->view ), FALSE ); font_desc = pango_font_description_from_string( "Monospace" ); gtk_widget_modify_font( log->view, font_desc ); pango_font_description_free( font_desc ); gtk_container_add( GTK_CONTAINER( swin ), log->view ); gtk_widget_show( log->view ); } static void log_class_init( LogClass *class ) { iWindowClass *iwindow_class = (iWindowClass *) class; parent_class = g_type_class_peek_parent( class ); iwindow_class->build = log_build; class->actions = NULL; class->n_actions = 0; class->toggle_actions = NULL; class->n_toggle_actions = 0; class->action_name = NULL; class->ui_description = NULL; class->menu_bar_name = NULL; } static void log_init( Log *log ) { } GtkType log_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Log", sizeof( Log ), sizeof( LogClass ), (GtkClassInitFunc) log_class_init, (GtkObjectInitFunc) log_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_IWINDOW, &info ); } return( type ); } void log_clear_action_cb( GtkAction *action, Log *log ) { GtkTextView *text_view = GTK_TEXT_VIEW( log->view ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); gtk_text_buffer_set_text( text_buffer, "", 0 ); } void log_text( Log *log, const char *buf ) { GtkTextView *text_view = GTK_TEXT_VIEW( log->view ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkTextMark *mark = gtk_text_buffer_get_insert( text_buffer ); GtkTextIter iter; gtk_text_buffer_get_end_iter( text_buffer, &iter ); gtk_text_buffer_move_mark( text_buffer, mark, &iter ); gtk_text_buffer_insert_at_cursor( text_buffer, buf, -1 ); gtk_text_view_scroll_to_mark( text_view, mark, 0.0, TRUE, 0.5, 1 ); #ifdef DEBUG_FILE printf( "%s", buf ); #endif } void log_textf( Log *log, const char *fmt, ... ) { va_list ap; char buf[MAX_STRSIZE]; va_start( ap, fmt ); (void) im_vsnprintf( buf, MAX_STRSIZE, fmt, ap ); va_end( ap ); log_text( log, buf ); } ================================================ FILE: src/log.h ================================================ /* Abstract base class for a log window: errors, link report, log, etc. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_LOG (log_get_type()) #define LOG( obj ) (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_LOG, Log )) #define LOG_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_LOG, LogClass)) #define IS_LOG( obj ) (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_LOG )) #define IS_LOG_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_LOG )) #define LOG_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_LOG, LogClass )) struct _Log { iWindow parent_class; GtkWidget *view; /* The textview we use to show the log */ }; typedef struct _LogClass { iWindowClass parent_class; /* How we want the menu bar built. */ GtkActionEntry *actions; int n_actions; GtkToggleActionEntry *toggle_actions; int n_toggle_actions; const char *action_name; const char *ui_description; const char *menu_bar_name; } LogClass; GtkType log_get_type( void ); void log_clear_action_cb( GtkAction *action, Log *log ); void log_text( Log *log, const char *buf ); void log_textf( Log *log, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); ================================================ FILE: src/main.c ================================================ /* main() ... start everything up. See mainw.c for main window stuff. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* Show all paint actions with flashing stuff. #define DEBUG_UPDATES */ /* Stop startup creation of externs for all VIPS functions etc. #define DEBUG_NOAUTO */ /* Stop on any gtk error/warning/whatever. Usually set by configure for dev * builds. #define DEBUG_FATAL */ /* But some themes can trigger warnings, argh, so sometimes we need to * undef it. VipsObject sets can trigger warnings. libgoffice will warn about * precision issues if run under valgrind. */ #undef DEBUG_FATAL /* Time startup. #define DEBUG_TIME */ /* On quit, make sure we free stuff we can free. #define DEBUG_LEAK */ /* Sometimes we need to be able to disable these at build time. #undef DEBUG_LEAK #undef DEBUG_FATAL */ /* General stuff. */ Workspaceroot *main_workspaceroot = NULL; /* All the workspaces */ Toolkitgroup *main_toolkitgroup = NULL; /* All the toolkits */ Symbol *main_symbol_root = NULL; /* Root of symtable */ Watchgroup *main_watchgroup = NULL; /* All of the watches */ Imageinfogroup *main_imageinfogroup = NULL; /* All of the images */ void *main_c_stack_base = NULL; /* Base of C stack */ gboolean main_starting = TRUE; /* In startup */ static const char *main_argv0 = NULL; /* argv[0] */ static iOpenFile *main_stdin = NULL; /* stdin as an iOpenFile */ static GtkIconFactory *main_icon_factory = NULL;/* Add stocks to this */ static char *main_option_script = NULL; static char *main_option_expression = NULL; gboolean main_option_batch = FALSE; static gboolean main_option_no_load_menus = FALSE; static gboolean main_option_no_load_args = FALSE; static gboolean main_option_stdin_ws = FALSE; static gboolean main_option_stdin_def = FALSE; static char *main_option_output = NULL; static char **main_option_set = NULL; static gboolean main_option_benchmark = FALSE; gboolean main_option_time_save = FALSE; gboolean main_option_profile = FALSE; gboolean main_option_i18n = FALSE; gboolean main_option_verbose = FALSE; static gboolean main_option_print_main = FALSE; static gboolean main_option_version = FALSE; static gboolean main_option_test = FALSE; static char *main_option_prefix = NULL; static GOptionEntry main_option[] = { { "expression", 'e', 0, G_OPTION_ARG_STRING, &main_option_expression, N_( "evaluate and print EXPRESSION" ), "EXPRESSION" }, { "script", 's', 0, G_OPTION_ARG_FILENAME, &main_option_script, N_( "load FILE as a set of definitions" ), "FILE" }, { "output", 'o', 0, G_OPTION_ARG_FILENAME, &main_option_output, N_( "write value of 'main' to FILE" ), "FILE" }, { "batch", 'b', 0, G_OPTION_ARG_NONE, &main_option_batch, N_( "run in batch mode" ), NULL }, { "set", '=', 0, G_OPTION_ARG_STRING_ARRAY, &main_option_set, N_( "set values" ), NULL }, { "verbose", 'V', 0, G_OPTION_ARG_NONE, &main_option_verbose, N_( "verbose error output" ), NULL }, { "no-load-menus", 'm', 0, G_OPTION_ARG_NONE, &main_option_no_load_menus, N_( "don't load menu definitions" ), NULL }, { "no-load-args", 'a', 0, G_OPTION_ARG_NONE, &main_option_no_load_args, N_( "don't try to load command-line arguments" ), NULL }, { "stdin-ws", 'w', 0, G_OPTION_ARG_NONE, &main_option_stdin_ws, N_( "load stdin as a workspace" ), NULL }, { "stdin-def", 'd', 0, G_OPTION_ARG_NONE, &main_option_stdin_def, N_( "load stdin as a set of definitions" ), NULL }, { "print-main", 'p', 0, G_OPTION_ARG_NONE, &main_option_print_main, N_( "print value of 'main' to stdout" ), NULL }, { "benchmark", 'c', 0, G_OPTION_ARG_NONE, &main_option_benchmark, N_( "start up and shut down" ), NULL }, { "time-save", 't', 0, G_OPTION_ARG_NONE, &main_option_time_save, N_( "time image save operations" ), NULL }, { "profile", 'r', 0, G_OPTION_ARG_NONE, &main_option_profile, N_( "profile workspace calculation" ), NULL }, { "prefix", 'x', 0, G_OPTION_ARG_FILENAME, &main_option_prefix, N_( "start as if installed to PREFIX" ), "PREFIX" }, { "i18n", 'i', 0, G_OPTION_ARG_NONE, &main_option_i18n, N_( "output strings for internationalisation" ), NULL }, { "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version, N_( "print version number" ), NULL }, { "test", 'T', 0, G_OPTION_ARG_NONE, &main_option_test, N_( "test for errors and quit" ), NULL }, { NULL } }; /* Accumulate startup errors here. */ static char main_start_error_txt[MAX_STRSIZE]; static VipsBuf main_start_error = VIPS_BUF_STATIC( main_start_error_txt ); static void main_log_add( const char *fmt, ... ) { va_list ap; va_start( ap, fmt ); vips_buf_vappendf( &main_start_error, fmt, ap ); va_end( ap ); } static const char * main_log_get( void ) { return( vips_buf_all( &main_start_error ) ); } static gboolean main_log_is_empty( void ) { return( vips_buf_is_empty( &main_start_error ) ); } /* NULL log handler. Used to suppress output on win32 without DEBUG_FATAL. */ #ifndef DEBUG_FATAL #ifdef OS_WIN32 static void main_log_null( const char *log_domain, GLogLevelFlags log_level, const char *message, void *user_data ) { } #endif /*OS_WIN32*/ #endif /*!DEBUG_FATAL*/ /* Print all errors and quit. Batch mode only. */ static void main_error_exit( const char *fmt, ... ) { va_list args; va_start( args, fmt ); (void) vfprintf( stderr, fmt, args ); va_end( args ); fprintf( stderr, "\n" ); if( strcmp( error_get_top(), "" ) != 0 ) { fprintf( stderr, "%s\n", error_get_top() ); if( strcmp( error_get_sub(), "" ) != 0 ) fprintf( stderr, "%s\n", error_get_sub() ); } if( main_option_verbose ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); slist_map( expr_error_all, (SListMapFn) expr_error_print, &buf ); fprintf( stderr, "%s", vips_buf_all( &buf ) ); } exit( 1 ); } /* Output a single main. */ static void main_print_main( Symbol *sym ) { PElement *root; root = &sym->expr->root; if( !symbol_recalculate_check( sym ) || !reduce_pelement( reduce_context, reduce_spine_strict, root ) ) main_error_exit( _( "error calculating \"%s\"" ), symbol_name_scope( sym ) ); if( main_option_output ) { char filename[FILENAME_MAX]; im_strncpy( filename, main_option_output, FILENAME_MAX ); if( !group_save_item( root, filename ) ) main_error_exit( _( "error saving \"%s\"" ), symbol_name_scope( sym ) ); } if( main_option_print_main ) graph_value( root ); } static void * main_print_ws( Workspace *ws, gboolean *found ) { Symbol *sym; if( (sym = compile_lookup( ws->sym->expr->compile, "main" )) ) { main_print_main( sym ); *found = TRUE; } return( NULL ); } /* Clean up our application and quit. Not interactive! Do any "has been * modified, OK to quit?" stuff before this, see main_quit_test(). */ static void main_quit( void ) { #if HAVE_FFTW || HAVE_FFTW3 iOpenFile *of; #endif /*HAVE_FFTW || HAVE_FFTW3*/ #ifdef DEBUG printf( "main_quit: cleaning up ...\n" ); #endif/*DEBUG*/ if( main_option_print_main || main_option_output ) { Symbol *sym; gboolean found; symbol_recalculate_all(); /* Process all the mains we can find: one at the top level, * one in each workspace. */ found = FALSE; if( (sym = compile_lookup( symbol_root->expr->compile, "main" )) ) { main_print_main( sym ); found = TRUE; } workspace_map( (workspace_map_fn) main_print_ws, &found, NULL ); if( !found ) main_error_exit( "%s", _( "no \"main\" found" ) ); } /* Force all our windows down. */ iwindow_map_all( (iWindowMapFn) iwindow_kill, NULL ); /* Saves recent and stuff like that. */ mainw_shutdown(); /* Dump wisdom back again. */ #if HAVE_FFTW || HAVE_FFTW3 if( (of = ifile_open_write( "%s" G_DIR_SEPARATOR_S "wisdom", get_savedir() )) ) { fftw_export_wisdom_to_file( of->fp ); ifile_close( of ); } #endif /*HAVE_FFTW*/ /* Remove any ws retain files. */ workspacegroup_autosave_clean(); /* Junk all symbols. This may remove a bunch of intermediate images * too. */ UNREF( main_watchgroup ); UNREF( main_symbol_root ); UNREF( main_toolkitgroup ); UNREF( main_workspaceroot ); /* Junk reduction machine ... this should remove all image temps. */ reduce_destroy( reduce_context ); #ifdef DEBUG_LEAK /* Free other GTK stuff. */ if( main_icon_factory ) gtk_icon_factory_remove_default( main_icon_factory ); junk_tooltips(); #ifdef HAVE_LIBGOFFICE /* Not quite sure what this does, but don't do it in batch mode. */ if( !main_option_batch ) libgoffice_shutdown (); #endif /*HAVE_LIBGOFFICE*/ path_rewrite_free_all(); /* Should have freed everything now. */ /* Make sure! FIXME ... #ifdef this lot out at some point */ UNREF( main_imageinfogroup ); heap_check_all_destroyed(); vips_shutdown(); managed_check_all_destroyed(); util_check_all_destroyed(); call_check_all_destroyed(); #endif /*DEBUG_LEAK*/ #ifdef DEBUG printf( "main_quit: exit( 0 )\n" ); #endif/*DEBUG*/ /* And exit. */ exit( 0 ); } /* We mustn't quit recursively! */ static gboolean main_quit_running = FALSE; static void main_quit_test_cb( void *sys, iWindowResult result ) { #ifdef DEBUG printf( "main_quit_test_cb:\n" ); #endif/*DEBUG*/ if( result == IWINDOW_YES ) /* No return from this. */ main_quit(); else /* Quit has been cancelled. */ main_quit_running = FALSE; } /* Check before quitting. */ void main_quit_test( void ) { if( main_quit_running ) { #ifdef DEBUG printf( "main_quit_test: recursive quit blocked\n" ); #endif/*DEBUG*/ return; } main_quit_running = TRUE; #ifdef DEBUG printf( "main_quit_test:\n" ); #endif/*DEBUG*/ /* Flush any pending preference saves before we look for dirty * objects. */ watchgroup_flush( main_watchgroup ); /* Close registered models. */ filemodel_inter_close_registered_cb( iwindow_pick_one(), NULL, main_quit_test_cb, NULL ); } static void main_watchgroup_changed_cb( void ) { /* Only set this in GUI mode. Otherwise, let the user control CPUs * with the env variable and --vips-concurrency args. */ if( !main_option_batch ) im_concurrency_set( VIPS_CPUS ); } /* Try to load a thing, anything at all. Actually, we don't load plugins * experimentally, win32 pops up an annoying error dialog if you try that. */ static gboolean main_load( Workspace *ws, const char *filename ) { Workspacegroup *new_wsg; if( (new_wsg = workspacegroup_new_from_file( main_workspaceroot, filename, filename )) ) { Mainw *mainw; if( !main_option_batch ) { mainw = mainw_new( new_wsg ); gtk_widget_show( GTK_WIDGET( mainw ) ); } mainw_recent_add( &mainw_recent_workspace, filename ); return( TRUE ); } error_clear(); /* workspace_load_file() needs to recalc to work, try to avoid that by * doing .defs first. */ if( is_file_type( &filesel_dfile_type, filename ) ) { if( toolkit_new_from_file( main_toolkitgroup, filename ) ) return( TRUE ); } /* Try as matrix or image. Have to do these via definitions. */ if( workspace_load_file( ws, filename ) ) return( TRUE ); error_clear(); error_top( _( "Unknown file type." ) ); error_sub( _( "Unable to load \"%s\"." ), filename ); return( FALSE ); } #ifndef DEBUG_NOAUTO static void * main_load_plug( char *name ) { if( !calli_string_filename( (calli_string_fn) im_load_plugin, name, NULL, NULL, NULL ) ) { error_top( _( "Unable to load." ) ); error_sub( _( "Error loading plug-in \"%s\"." ), name ); error_vips(); iwindow_alert( NULL, GTK_MESSAGE_ERROR ); } return( NULL ); } #endif /*!DEBUG_NOAUTO*/ static void * main_load_def( const char *filename ) { Toolkit *kit; if( !main_option_no_load_menus || im_skip_dir( filename )[0] == '_' ) { progress_update_loading( 0, im_skip_dir( filename ) ); if( !(kit = toolkit_new_from_file( main_toolkitgroup, filename )) ) iwindow_alert( NULL, GTK_MESSAGE_ERROR ); else filemodel_set_auto_load( FILEMODEL( kit ) ); } return( NULL ); } static void * main_load_wsg( const char *filename ) { Workspacegroup *wsg; #ifdef DEBUG printf( "main_load_wsg: %s\n", filename ); #endif/*DEBUG*/ progress_update_loading( 0, im_skip_dir( filename ) ); if( !(wsg = workspacegroup_new_from_file( main_workspaceroot, filename, filename )) ) iwindow_alert( NULL, GTK_MESSAGE_ERROR ); else { filemodel_set_auto_load( FILEMODEL( wsg ) ); } return( NULL ); } #ifndef DEBUG_NOAUTO /* Link all the packages in a function. */ static void * main_link_package( im_package *pack) { char name[MAX_STRSIZE]; Toolkit *kit; int i; im_snprintf( name, MAX_STRSIZE, "_%s", pack->name ); kit = toolkit_new( main_toolkitgroup, name ); for( i = 0; i < pack->nfuncs; i++ ) if( call_is_callable( pack->table[i] ) ) { Symbol *sym; sym = symbol_new( symbol_root->expr->compile, pack->table[i]->name ); g_assert( sym->type == SYM_ZOMBIE ); sym->type = SYM_EXTERNAL; sym->function = pack->table[i]; sym->fn_nargs = call_n_args( pack->table[i] ); (void) tool_new_sym( kit, -1, sym ); symbol_made( sym ); } filemodel_set_auto_load( FILEMODEL( kit ) ); filemodel_set_modified( FILEMODEL( kit ), FALSE ); kit->pseudo = TRUE; return( NULL ); } #endif /*!DEBUG_NOAUTO*/ /* Load all plugins and defs. */ static void main_load_startup( void ) { mainw_recent_freeze(); /* Stop load of builtins, plugs and vips ... handy for debugging if you're * tracing symbol.c */ #ifdef DEBUG_NOAUTO printf( "*** DEBUG_NOAUTO set, not loading builtin, plugs and vips\n" ); #else /*!DEBUG_NOAUTO*/ #ifdef DEBUG printf( "built-ins init\n" ); #endif/*DEBUG*/ /* Add builtin toolkit. */ builtin_init(); #ifdef DEBUG printf( "plug-ins init\n" ); #endif/*DEBUG*/ /* Load any plug-ins on PATH_START. */ (void) path_map( PATH_START, "*.plg", (path_map_fn) main_load_plug, NULL ); /* Link all VIPS functions as SYM_EXTERNAL. */ (void) im_map_packages( (VSListMap2Fn) main_link_package, NULL ); #endif /*!DEBUG_NOAUTO*/ /* Load up all defs and wses. */ #ifdef DEBUG printf( "definitions init\n" ); #endif/*DEBUG*/ (void) path_map( PATH_START, "*.def", (path_map_fn) main_load_def, NULL ); #ifdef DEBUG printf( "ws init\n" ); #endif/*DEBUG*/ (void) path_map( PATH_START, "*.ws", (path_map_fn) main_load_wsg, NULL ); mainw_recent_thaw(); } static void * main_junk_auto_load( Filemodel *filemodel ) { g_assert( IS_FILEMODEL( filemodel ) ); if( filemodel->auto_load ) IDESTROY( filemodel ); return( NULL ); } /* Remove and reload all menus/plugins/workspaces. */ void main_reload( void ) { progress_begin(); /* Remove. */ toolkitgroup_map( main_toolkitgroup, (toolkit_map_fn) main_junk_auto_load, NULL, NULL ); workspace_map( (workspace_map_fn) main_junk_auto_load, NULL, NULL ); im_close_plugins(); /* Reload. */ main_load_startup(); /* We may have changed our prefs ... link the watches to the * new prefs workspace. */ watch_relink_all(); progress_end(); } /* Use a file to paint a named stock item. */ static void main_file_for_stock( GtkIconFactory *icon_factory, const char *stock, const char *file ) { GtkIconSource *icon_source; GtkIconSet *icon_set; char buf[FILENAME_MAX]; im_snprintf( buf, FILENAME_MAX, "$VIPSHOME/share/$PACKAGE/data/%s", file ); path_expand( buf ); icon_source = gtk_icon_source_new(); gtk_icon_source_set_filename( icon_source, buf ); icon_set = gtk_icon_set_new(); gtk_icon_set_add_source( icon_set, icon_source ); gtk_icon_source_free( icon_source ); gtk_icon_factory_add( icon_factory, stock, icon_set ); gtk_icon_set_unref( icon_set ); } /* Make our custom icon sets. */ static void main_register_icons( void ) { static const GtkStockItem stock_item[] = { /* Can be (eg.) * * { GTK_STOCK_COPY, N_("_Copy"), GDK_CONTROL_MASK, 'c', GETTEXT_PACKAGE }, * */ { STOCK_NEXT_ERROR, N_( "Next _Error" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_DROPPER, N_( "Ink dropper" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_DUPLICATE, N_( "D_uplicate" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_PAINTBRUSH, N_( "Pen" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_LINE, N_( "Line" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_TEXT, N_( "Text" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_SMUDGE, N_( "Smudge" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_FLOOD, N_( "Flood" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_FLOOD_BLOB, N_( "Flood Blob" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_RECT, N_( "Fill Rectangle" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_MOVE, N_( "Pan" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_SELECT, N_( "Select" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_LOCK, N_( "Locked" ), 0, 0, GETTEXT_PACKAGE }, /* And the LEDs we use. */ { STOCK_LED_RED, N_( "Red LED" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_LED_GREEN, N_( "Green LED" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_LED_BLUE, N_( "Blue LED" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_LED_YELLOW, N_( "Yellow LED" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_LED_CYAN, N_( "Cyan LED" ), 0, 0, GETTEXT_PACKAGE }, { STOCK_LED_OFF, N_( "Off LED" ), 0, 0, GETTEXT_PACKAGE } }; GtkIconSet *icon_set; gtk_stock_add_static( stock_item, IM_NUMBER( stock_item ) ); main_icon_factory = gtk_icon_factory_new(); /* Make a colour picker stock ... take the stock icon and add our own * text (gtk defines no text for the standard version of this stock * icon). */ icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_COLOR_PICKER ); gtk_icon_factory_add( main_icon_factory, STOCK_DROPPER, icon_set ); /* For Next Error, use JUMP_TO. */ icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_JUMP_TO ); gtk_icon_factory_add( main_icon_factory, STOCK_NEXT_ERROR, icon_set ); /* For clone, use the DND_MULTIPLE icon (close enough). */ icon_set = gtk_icon_factory_lookup_default( GTK_STOCK_DND_MULTIPLE ); gtk_icon_factory_add( main_icon_factory, STOCK_DUPLICATE, icon_set ); /* Link to our stock .pngs. */ main_file_for_stock( main_icon_factory, STOCK_PAINTBRUSH, "stock-tool-ink-22.png" ); main_file_for_stock( main_icon_factory, STOCK_LINE, "stock-tool-path-22.png" ); main_file_for_stock( main_icon_factory, STOCK_TEXT, "stock-tool-text-22.png" ); main_file_for_stock( main_icon_factory, STOCK_SMUDGE, "stock-tool-smudge-22.png" ); main_file_for_stock( main_icon_factory, STOCK_FLOOD, "stock-tool-bucket-fill-22.png" ); main_file_for_stock( main_icon_factory, STOCK_FLOOD_BLOB, "stock-tool-bucket-fill-22.png" ); main_file_for_stock( main_icon_factory, STOCK_RECT, "stock-tool-rect-select-22.png" ); main_file_for_stock( main_icon_factory, STOCK_MOVE, "stock-tool-move-22.png" ); main_file_for_stock( main_icon_factory, STOCK_SELECT, "stock-tool-select-22.png" ); main_file_for_stock( main_icon_factory, STOCK_LOCK, "stock-padlock-closed-22.png" ); main_file_for_stock( main_icon_factory, STOCK_ALERT, "stock-alert-22.png" ); main_file_for_stock( main_icon_factory, STOCK_LED_RED, "stock-led-red-18.png" ); main_file_for_stock( main_icon_factory, STOCK_LED_GREEN, "stock-led-green-18.png" ); main_file_for_stock( main_icon_factory, STOCK_LED_BLUE, "stock-led-blue-18.png" ); main_file_for_stock( main_icon_factory, STOCK_LED_YELLOW, "stock-led-yellow-18.png" ); main_file_for_stock( main_icon_factory, STOCK_LED_CYAN, "stock-led-cyan-18.png" ); main_file_for_stock( main_icon_factory, STOCK_LED_OFF, "stock-led-off-18.png" ); gtk_icon_factory_add_default( main_icon_factory ); g_object_unref( main_icon_factory ); } /* Init the display connection stuff. */ static void main_x_init( int *argc, char ***argv ) { char buf[FILENAME_MAX]; #ifdef DEBUG printf( "X11 init\n" ); #endif/*DEBUG*/ (void) calli_string_filename( (calli_string_fn) gtk_rc_add_default_file, "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "rc" G_DIR_SEPARATOR_S "ipgtkrc", NULL, NULL, NULL ); gtk_init( argc, argv ); /* Set the default icon. */ im_strncpy( buf, "$VIPSHOME/share/$PACKAGE/data/vips-128.png", FILENAME_MAX ); path_expand( buf ); gtk_window_set_default_icon_from_file( buf, NULL ); /* Turn off startup notification. Startup is done when we pop our * first window, not when we make this secret window. */ gtk_window_set_auto_startup_notification( FALSE ); #ifdef DEBUG_UPDATES printf( "*** debug updates is on\n" ); gdk_window_set_debug_updates( TRUE ); #endif /*DEBUG_UPDATES*/ main_register_icons(); /* Next window we make is end of startup. */ gtk_window_set_auto_startup_notification( TRUE ); /* Load up any saved accelerators. */ calli_string_filenamef( (calli_string_fn) gtk_accel_map_load, "%s" G_DIR_SEPARATOR_S "accel_map", get_savedir() ); } static void * main_toobig_done_sub( const char *filename ) { unlinkf( "%s", filename ); return( NULL ); } /* OK in "flush temps" yesno. */ static void main_toobig_done( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { /* Don't "rm *", too dangerous. */ path_map_dir( PATH_TMP, "*.v", (path_map_fn) main_toobig_done_sub, NULL ); path_map_dir( PATH_TMP, "*.ws", (path_map_fn) main_toobig_done_sub, NULL ); /* _stdenv.def:magick can generate .tif files. */ path_map_dir( PATH_TMP, "*.tif", (path_map_fn) main_toobig_done_sub, NULL ); /* autotrace can make some others. */ path_map_dir( PATH_TMP, "*.ppm", (path_map_fn) main_toobig_done_sub, NULL ); path_map_dir( PATH_TMP, "*.svg", (path_map_fn) main_toobig_done_sub, NULL ); /* Tell space-free indicators to update. */ if( main_imageinfogroup ) iobject_changed( IOBJECT( main_imageinfogroup ) ); nfn( sys, IWINDOW_YES ); } /* Test for a bunch of stuff in the TMP area. Need to do this before * we load args in case there are large JPEGs there. Only bother in * interactive mode: we won't be able to question the user without an * X connection. */ static void main_check_temp( double total ) { if( total > 10 * 1024 * 1024 ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char tmp[FILENAME_MAX]; im_strncpy( tmp, PATH_TMP, FILENAME_MAX ); path_expand( tmp ); vips_buf_append_size( &buf, total ); box_yesno( NULL, main_toobig_done, iwindow_true_cb, NULL, NULL, NULL, _( "Empty temp area" ), _( "Many files in temp area." ), _( "The temp area \"%s\" contains %s of files. " "Would you like to empty the temp area? " "This will delete any workspace backups and " "cannot be undone." ), tmp, vips_buf_all( &buf ) ); } } /* Make sure a savedir exists. Used to build the "~/.nip2-xx/tmp" etc. * directory tree. */ static void main_mkdir( const char *dir ) { if( !existsf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), dir ) ) if( !mkdirf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), dir ) ) error_exit( _( "unable to make %s %s: %s" ), get_savedir(), dir, g_strerror( errno ) ); } static gboolean main_set( const char *str ) { Symbol *sym; attach_input_string( str ); if( !(sym = parse_set_symbol()) ) return( FALSE ); /* Put the input just after the '=', ready to parse a RHS into the * symbol. */ attach_input_string( str + IM_CLIP( 0, input_state.charpos - 1, strlen( str ) ) ); if( !symbol_user_init( sym ) || !parse_rhs( sym->expr, PARSE_RHS ) ) { /* Another parse error. */ expr_error_get( sym->expr ); /* Block changes to error_string ... symbol_destroy() * can set this for compound objects. */ error_block(); IDESTROY( sym ); error_unblock(); return( FALSE ); } symbol_made( sym ); /* Is there a row? Make sure any modified text there can't zap our new * text. */ if( sym->expr->row ) { Row *row = sym->expr->row; heapmodel_set_modified( HEAPMODEL( row->child_rhs->itext ), FALSE ); } return( TRUE ); } static char prefix_buffer[FILENAME_MAX]; static gboolean prefix_valid = FALSE; /* Override the install guess from vips. Handy for testing. */ static void set_prefix( const char *prefix ) { im_strncpy( prefix_buffer, prefix, FILENAME_MAX ); nativeize_path( prefix_buffer ); absoluteize_path( prefix_buffer ); setenvf( "VIPSHOME", "%s", prefix_buffer ); prefix_valid = TRUE; } /* Guess VIPSHOME, if we can. */ const char * get_prefix( void ) { if( !prefix_valid ) { const char *prefix; if( !(prefix = im_guess_prefix( main_argv0, "VIPSHOME" )) ) { error_top( _( "Unable to find install area." ) ); error_vips(); return( NULL ); } set_prefix( prefix ); } return( prefix_buffer ); } /* Start here! */ int main( int argc, char *argv[] ) { gboolean welcome_message = FALSE; Workspacegroup *wsg; Workspace *ws; GError *error = NULL; GOptionContext *context; const char *prefix; int i; double total = 0.0; #ifdef HAVE_GETRLIMIT struct rlimit rlp; #endif /*HAVE_GETRLIMIT*/ char name[256]; #if HAVE_FFTW || HAVE_FFTW3 iOpenFile *of; #endif /*HAVE_FFTW*/ Toolkit *kit; char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); #ifdef DEBUG_TIME GTimer *startup_timer = g_timer_new(); printf( "DEBUG_TIME: startup timer zeroed ...\n" ); #endif /*DEBUG_TIME*/ /* In startup phase. */ main_starting = TRUE; /* Want numeric locale to be "C", so we have C rules for doing * double <-> string (ie. no "," for decimal point). */ setlocale( LC_ALL, "" ); setlocale( LC_NUMERIC, "C" ); /* Make sure our LC_NUMERIC setting is not trashed. */ gtk_disable_setlocale(); #ifdef DEBUG printf( "main: sizeof( HeapNode ) == %zd\n", sizeof( HeapNode ) ); /* Should be 3 pointers, hopefully. */ if( sizeof( HeapNode ) != 3 * sizeof( void * ) ) printf( "*** struct packing problem!\n" ); #endif/*DEBUG*/ /* Yuk .. shouldn't really write to argv0. This can't change the * string length. * * On win32 we will sometimes get paths with mixed '/' and '\' which * confuses vips's prefix guessing. Make sure we have one or the other. */ nativeize_path( argv[0] ); main_argv0 = argv[0]; main_c_stack_base = &argc; /* Pass config.h stuff down to .ws files. */ setenvf( "PACKAGE", "%s", PACKAGE ); setenvf( "VERSION", "%s", VERSION ); #ifdef OS_WIN32 { /* No HOME on windows ... make one from HOMEDRIVE and HOMEDIR (via * glib). */ const char *home; char buf[FILENAME_MAX]; if( !(home = g_getenv( "HOME" )) ) home = g_get_home_dir(); /* We need native paths. */ strncpy( buf, home, FILENAME_MAX ); nativeize_path( buf ); setenvf( "HOME", "%s", buf ); } #endif /*OS_WIN32*/ /* Name of the dir we store our config stuff in. This can get used by * Preferences.ws. */ setenvf( "SAVEDIR", "%s", get_savedir() ); /* Path separator on this platform. */ setenvf( "SEP", "%s", G_DIR_SEPARATOR_S ); /* Executable file extension (eg. ".exe" on Windows). */ setenvf( "EXEEXT", "%s", VIPS_EXEEXT ); /* Start up vips. */ if( im_init_world( main_argv0 ) ) error_exit( "unable to start VIPS" ); /* The vips8 cache is no use to us. We have our own cache which is * integrated with our invalidate system. */ vips_cache_set_max( 0 ); /* Init i18n ... get catalogues from $VIPSHOME/share/locale so we're * relocatable. */ prefix = get_prefix(); im_snprintf( name, 256, "%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale", prefix ); #ifdef DEBUG printf( "bindtextdomain: %s\n", name ); #endif /*DEBUG*/ textdomain( GETTEXT_PACKAGE ); bindtextdomain( GETTEXT_PACKAGE, name ); bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" ); /* Set localised application name. */ g_set_application_name( _( PACKAGE ) ); context = g_option_context_new( _( "- image processing spreadsheet" ) ); g_option_context_add_main_entries( context, main_option, GETTEXT_PACKAGE ); /* Don't start X here! We may be in batch mode. */ g_option_context_add_group( context, gtk_get_option_group( FALSE ) ); g_option_context_add_group( context, im_get_option_group() ); if( !g_option_context_parse( context, &argc, &argv, &error ) ) vfatal( &error ); g_option_context_free( context ); /* Override the install guess from vips. This won't pick up msg * cats sadly :( since we have to init i18n before arg parsing. Handy * for testing without installing. */ if( main_option_prefix ) set_prefix( main_option_prefix ); if( main_option_version ) { printf( "%s-%s", PACKAGE, VERSION ); printf( "\n" ); printf( _( "linked to vips-%s" ), im_version_string() ); printf( "\n" ); exit( 0 ); } #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 ); #else /*!DEBUG_FATAL*/ #ifdef OS_WIN32 /* No logging output ... on win32, log output pops up a very annoying * console text box. */ g_log_set_handler( "GLib", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, main_log_null, NULL ); g_log_set_handler( "Gtk", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, main_log_null, NULL ); g_log_set_handler( NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, main_log_null, NULL ); #endif /*OS_WIN32*/ #endif /*DEBUG_FATAL*/ main_stdin = ifile_open_read_stdin(); #ifdef HAVE_GETRLIMIT /* Make sure we have lots of file descriptors. Some platforms have cur * as 256 and max at 1024 to keep stdio happy. */ if( getrlimit( RLIMIT_NOFILE, &rlp ) == 0 ) { rlim_t old_limit = rlp.rlim_cur; rlp.rlim_cur = rlp.rlim_max; if( setrlimit( RLIMIT_NOFILE, &rlp ) == 0 ) { #ifdef DEBUG printf( "set max file descriptors to %d\n", (int) rlp.rlim_max ); #endif /*DEBUG*/ } else if( (int) rlp.rlim_max != -1 ) { /* -1 means can't-be-set, at least on os x, so don't * warn. */ g_warning( _( "unable to change max file descriptors\n" "max file descriptors still set to %d" ), (int) old_limit ); } } else { g_warning( _( "unable to read max file descriptors" ) ); } #endif /*HAVE_GETRLIMIT*/ /* Make our file types. */ filesel_startup(); /* Set default values for paths. */ path_init(); /* First time we've been run? Welcome message. */ if( !existsf( "%s", get_savedir() ) ) welcome_message = TRUE; /* Always make these in case some got deleted. */ main_mkdir( "" ); main_mkdir( "tmp" ); main_mkdir( "start" ); main_mkdir( "data" ); /* Init other stuff. */ #ifdef HAVE_FFTW3 fftw_import_system_wisdom(); #endif /*HAVE_FFTW3*/ #if HAVE_FFTW || HAVE_FFTW3 if( (of = ifile_open_read( "%s" G_DIR_SEPARATOR_S "wisdom", get_savedir() )) ) { fftw_import_wisdom_from_file( of->fp ); ifile_close( of ); } #endif /*HAVE_FFTW*/ mainw_startup(); reduce_context = reduce_new(); main_symbol_root = symbol_root_init(); g_object_ref( G_OBJECT( main_symbol_root ) ); iobject_sink( IOBJECT( main_symbol_root ) ); model_base_init(); main_workspaceroot = workspaceroot_new( "Workspaces" ); g_object_ref( G_OBJECT( main_workspaceroot ) ); iobject_sink( IOBJECT( main_workspaceroot ) ); main_watchgroup = watchgroup_new( main_workspaceroot, "Preferences" ); g_object_ref( G_OBJECT( main_watchgroup ) ); iobject_sink( IOBJECT( main_watchgroup ) ); main_toolkitgroup = toolkitgroup_new( symbol_root ); g_object_ref( G_OBJECT( main_toolkitgroup ) ); iobject_sink( IOBJECT( main_toolkitgroup ) ); main_imageinfogroup = imageinfogroup_new(); g_object_ref( G_OBJECT( main_imageinfogroup ) ); iobject_sink( IOBJECT( main_imageinfogroup ) ); /* First pass at command-line options. Just look at the flags that * imply other flags, don't do any processing yet. */ if( main_option_script ) { main_option_batch = TRUE; main_option_no_load_menus = TRUE; main_option_no_load_args = TRUE; main_option_print_main = TRUE; } if( main_option_test ) { main_option_batch = TRUE; main_option_verbose = TRUE; } if( main_option_expression ) { main_option_batch = TRUE; main_option_no_load_menus = TRUE; main_option_no_load_args = TRUE; main_option_print_main = TRUE; } if( main_option_benchmark ) { main_option_batch = TRUE; main_option_no_load_menus = FALSE; } if( main_option_i18n ) { /* Just start up and shutdown, no X. Output constant * i18n strings. */ main_option_batch = TRUE; main_option_no_load_menus = FALSE; } #ifdef DEBUG if( main_option_batch ) printf( "non-interactive mode\n" ); #endif /*DEBUG*/ /* Start the X connection. We need this before _load_all(), so that * we can pop up error dialogs. */ if( !main_option_batch ) main_x_init( &argc, &argv ); #ifdef HAVE_LIBGOFFICE libgoffice_init(); go_plugins_init( NULL, NULL, NULL, NULL, TRUE, GO_TYPE_PLUGIN_LOADER_MODULE ); #endif /*HAVE_LIBGOFFICE*/ /* Load start-up stuff. Builtins, plugins, externals etc. We need to * do this before we load any user code so we can prevent redefinition * of builtins. */ main_load_startup(); /* Recalc to build all classes and gets prefs working. * * We have to do this in batch * mode since we can find dirties through dynamic lookups. Even though * you might think we could just follow recomps. */ symbol_recalculate_all_force( TRUE ); #ifdef DEBUG printf( "arg processing\n" ); #endif/*DEBUG*/ /* Might make this from stdin/whatever if we have a special * command-line flag. */ wsg = NULL; ws = NULL; /* Second command-line pass. This time we do any actions. */ if( main_option_script ) { if( !toolkit_new_from_file( main_toolkitgroup, main_option_script ) ) main_log_add( "%s\n", error_get_sub() ); } if( main_option_expression ) { kit = toolkit_new( main_toolkitgroup, "_expression" ); vips_buf_appendf( &buf, "main = %s;", main_option_expression ); attach_input_string( vips_buf_all( &buf ) ); (void) parse_onedef( kit, -1 ); filemodel_set_modified( FILEMODEL( kit ), FALSE ); } if( main_option_stdin_def ) { if( !(kit = toolkit_new_from_openfile( main_toolkitgroup, main_stdin )) ) main_log_add( "%s\n", error_get_sub() ); } if( main_option_stdin_ws ) { if( !(wsg = workspacegroup_new_from_openfile( main_workspaceroot, main_stdin )) ) main_log_add( "%s\n", error_get_sub() ); else /* Don't want to have "stdin" as the filename. */ filemodel_set_filename( FILEMODEL( wsg ), NULL ); } /* Make a start workspace and workspacegroup to load * stuff into. */ if( !wsg ) { wsg = workspacegroup_new_blank( main_workspaceroot, NULL ); ws = WORKSPACE( icontainer_get_nth_child( ICONTAINER( wsg ), 0 ) ); } /* Reset IM_CONCURRENCY if a watch changes. Need to do this after * parsing options so we skip in batch mode. */ g_signal_connect( main_watchgroup, "watch_changed", G_CALLBACK( main_watchgroup_changed_cb ), NULL ); /* Pass PATH_TMP down to vips via TMPDIR. See im_system(), for * example. We need to do this after the first recomp so that prefs * are loaded. */ { char buf[FILENAME_MAX]; im_strncpy( buf, PATH_TMP, FILENAME_MAX ); path_expand( buf ); setenvf( "TMPDIR", "%s", buf ); path_rewrite_add( PATH_TMP, "$TMPDIR", TRUE ); } /* Measure amount of stuff in temp area ... need this for checking * temps later. We pop a dialog if there are too many, so only useful * in interactive mode. */ if( !main_option_batch ) total = directory_size( PATH_TMP ); /* Make nip's argc/argv[]. */ kit = toolkit_new( main_toolkitgroup, "_args" ); vips_buf_rewind( &buf ); vips_buf_appendf( &buf, "argc = %d;", argc ); attach_input_string( vips_buf_all( &buf ) ); (void) parse_onedef( kit, -1 ); vips_buf_rewind( &buf ); vips_buf_appendf( &buf, "argv = [" ); for( i = 0; i < argc; i++ ) { /* Ignore "--" args. Consider eg. * * ./try201.nip2 -o x.v -- -12 ~/pics/shark.jpg * * if we didn't remove --, all scripts would need to. */ if( strcmp( argv[i], "--" ) == 0 ) continue; if( i > 0 ) vips_buf_appendf( &buf, ", " ); vips_buf_appendf( &buf, "\"%s\"", argv[i] ); } vips_buf_appendf( &buf, "];" ); attach_input_string( vips_buf_all( &buf ) ); if( !parse_onedef( kit, -1 ) ) main_log_add( "%s\n", error_get_sub() ); filemodel_set_modified( FILEMODEL( kit ), FALSE ); /* Double-check: we often forget to move the prefs ws to the latest * version. */ #ifdef DEBUG_LEAK { Symbol *wsr_sym = main_workspaceroot->sym; Symbol *ws_sym = SYMBOL( icontainer_child_lookup( ICONTAINER( wsr_sym->expr->compile ), "Preferences" ) ); if( !ws_sym ) printf( "No prefs workspace!\n" ); else { Workspace *ws = ws_sym->ws; if( ws->compat_major || ws->compat_minor ) printf( "Preferences loaded in compat mode!\n" ); } } #endif /*DEBUG_LEAK*/ if( !main_option_no_load_args ) { /* Load args as files, if we can. */ for( i = 1; i < argc; i++ ) { char buf[FILENAME_MAX]; /* We want to use the absolute, compact form of the * filename object so we don't get a dependency on CWD. */ im_strncpy( buf, argv[i], FILENAME_MAX ); path_compact( buf ); if( !main_load( ws, buf ) ) main_log_add( "%s\n", error_get_sub() ); } } /* In batch mode give up if there are startup errors. */ if( main_option_batch ) { if( !main_log_is_empty() ) { fprintf( stderr, _( "Startup error log:\n%s" ), main_log_get() ); exit( 1 ); } } if( main_option_set ) { int i; for( i = 0; main_option_set[i]; i++ ) { if( main_option_verbose ) printf( "main_set: %s\n", main_option_set[i] ); if( !main_set( main_option_set[i] ) ) main_log_add( "%s\n%s", error_get_top(), error_get_sub() ); } } /* Make sure our start ws doesn't have modified set. We may have * loaded some images or whatever into it. */ workspace_set_modified( ws, FALSE ); /* If the start ws is empty (we didn't load anything into it) and we * loaded some other workspaces, we can junk the empty ws. */ if( icontainer_get_n_children( ICONTAINER( main_workspaceroot ) ) > 2 && workspace_is_empty( ws ) ) { IDESTROY( wsg ); wsg = NULL; ws = NULL; } #ifdef DEBUG_TIME printf( "DEBUG_TIME: main init in %gs\n", g_timer_elapsed( startup_timer, NULL ) ); #endif /*DEBUG_TIME*/ /* Are we running interactively? Start the main window and loop. */ if( !main_option_batch ) { if( wsg ) { Mainw *mainw; mainw = mainw_new( wsg ); gtk_widget_show( GTK_WIDGET( mainw ) ); } /* Process a few events ... we want the window to be mapped so * that log/welcome/clean? messages we pop appear in the right * place on the screen. */ while( g_main_context_iteration( NULL, FALSE ) ) ; if( !main_log_is_empty() ) { error_top( _( "Startup error." ) ); error_sub( _( "Startup error log:\n%s" ), main_log_get() ); iwindow_alert( NULL, GTK_MESSAGE_ERROR ); } if( welcome_message ) { char save_dir[FILENAME_MAX]; char buf[256]; im_snprintf( buf, 256, _( "Welcome to %s-%s!" ), PACKAGE, VERSION ); im_strncpy( save_dir, get_savedir(), FILENAME_MAX ); path_expand( save_dir ); error_top( "%s", buf ); error_sub( _( "A new directory has been created to hold startup, " "data and temporary files:\n\n" " %s\n\n" "If you've used previous versions of %s, you might want " "to copy files over from your old work area." ), save_dir, PACKAGE ); iwindow_alert( NULL, GTK_MESSAGE_INFO ); } main_check_temp( total ); #ifdef DEBUG printf( "starting event dispatch loop\n" ); #endif/*DEBUG*/ main_starting = FALSE; symbol_recalculate_all_force( FALSE ); gtk_main(); } if( main_option_test ) { /* Make sure we've had at least one recomp. */ symbol_recalculate_all_force( TRUE ); if( expr_error_all ) main_error_exit( "--test: errors found" ); } /* No return from this. */ main_quit(); return( 0 ); } #ifdef OS_WIN32 /* Get non-cmd line args on win32. */ static int breakargs( char *program, char *line, char **argv ) { int argc = 1; argv[0] = program; while( *line && argc < MAX_SYSTEM - 1 ) { while( *line && isspace( *line ) ) line++; if( *line == '"' ) { /* Windows-95 quoted arguments */ char *start = line + 1; char *end = start; while( *end && *end != '"' ) end++; if( *end == '"' ) { *end = '\0'; argv[argc++] = start; line = end + 1; continue; } } if( *line ) { argv[argc++] = line; while( *line && !isspace( *line ) ) line++; if( *line ) *line++ = '\0'; } } /* add trailing NULL pointer to argv */ argv[argc] = NULL; return( argc ); } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nShowCmd ) { char *argv[MAX_SYSTEM]; int argc; TCHAR program[MAXPATHLEN]; GetModuleFileName( hInstance, program, sizeof(program) ); argc = breakargs( (char *) program, lpszCmdLine, argv ); return( main( argc, argv ) ); } #endif /*OS_WIN32*/ ================================================ FILE: src/main.h ================================================ /* Declarations supporting main.c. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ extern Workspaceroot *main_workspaceroot; /* All the workspaces */ extern Toolkitgroup *main_toolkitgroup; /* All the toolkits */ extern Symbol *main_symbol_root; /* Root of symtable */ extern Watchgroup *main_watchgroup; /* All of the watches */ extern Imageinfogroup *main_imageinfogroup; /* All of the images */ extern void *main_c_stack_base; /* Base of C stack */ extern gboolean main_starting; /* In startup */ extern gboolean main_option_time_save; /* Time save image ops */ extern gboolean main_option_profile; /* Profile calcualtion */ extern gboolean main_option_i18n; /* Output i18n strings */ extern gboolean main_option_batch; /* Running in batch mode */ extern gboolean main_option_verbose; /* Verbose output */ /* Styles for buttons etc. */ extern GtkStyle *default_style; extern GtkStyle *selected_style; extern GtkStyle *error_style; extern GtkStyle *ok_style; extern GtkStyle *tooltip_style; extern GtkStyle *leaf_style; extern GtkStyle *dirty_style; void main_quit_test( void ); void main_reload( void ); const char *get_prefix( void ); ================================================ FILE: src/mainw.c ================================================ /* main processing window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* Load and save recent items here. */ #define RECENT_WORKSPACE "recent_workspace" #define RECENT_IMAGE "recent_image" #define RECENT_MATRIX "recent_matrix" /* Recently loaded/saved workspaces, images and matricies. */ GSList *mainw_recent_workspace = NULL; GSList *mainw_recent_image = NULL; GSList *mainw_recent_matrix = NULL; /* Auto-recalc state. Don't do this as a preference, since preferences are * workspaces and need to have recalc working to operate. */ gboolean mainw_auto_recalc = TRUE; static gint mainw_layout_timeout = 0; static iWindowClass *parent_class = NULL; /* All the mainw. */ static GSList *mainw_all = NULL; void mainw_startup( void ) { IM_FREEF( recent_free, mainw_recent_workspace ); IM_FREEF( recent_free, mainw_recent_image ); IM_FREEF( recent_free, mainw_recent_matrix ); mainw_recent_workspace = recent_load( RECENT_WORKSPACE ); mainw_recent_image = recent_load( RECENT_IMAGE ); mainw_recent_matrix = recent_load( RECENT_MATRIX ); } void mainw_shutdown( void ) { recent_save( mainw_recent_workspace, RECENT_WORKSPACE ); recent_save( mainw_recent_image, RECENT_IMAGE ); recent_save( mainw_recent_matrix, RECENT_MATRIX ); IM_FREEF( recent_free, mainw_recent_workspace ); IM_FREEF( recent_free, mainw_recent_image ); IM_FREEF( recent_free, mainw_recent_matrix ); } static int mainw_recent_freeze_count = 0; void mainw_recent_freeze( void ) { mainw_recent_freeze_count += 1; } void mainw_recent_thaw( void ) { g_assert( mainw_recent_freeze_count > 0 ); mainw_recent_freeze_count -= 1; } void mainw_recent_add( GSList **recent, const char *filename ) { if( !mainw_recent_freeze_count ) { char buf[FILENAME_MAX]; im_strncpy( buf, PATH_TMP, FILENAME_MAX ); path_expand( buf ); if( filename && strcmp( filename, "" ) != 0 && !is_prefix( buf, filename ) ) *recent = recent_add( *recent, filename ); } } /* Pick a mainw at random. Used if we need a window for a dialog, and we're * not sure which to pick. */ Mainw * mainw_pick_one( void ) { if( !mainw_all ) /* Must be a cast here, since iwindow_pick_one() can return * NULL during shutdown. */ return( (Mainw *) iwindow_pick_one() ); return( MAINW( mainw_all->data ) ); } static void mainw_finalize( GObject *gobject ) { #ifdef DEBUG printf( "mainw_finalize: %p\n", gobject ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_MAINW( gobject ) ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void mainw_dispose( GObject *object ) { Mainw *mainw; g_return_if_fail( object != NULL ); g_return_if_fail( IS_MAINW( object ) ); mainw = MAINW( object ); #ifdef DEBUG printf( "mainw_dispose\n" ); #endif /*DEBUG*/ IM_FREEF( g_source_remove, mainw->refresh_timeout ); FREESID( mainw->changed_sid, mainw->wsg ); FREESID( mainw->imageinfo_changed_sid, main_imageinfogroup ); FREESID( mainw->heap_changed_sid, reduce_context->heap ); FREESID( mainw->watch_changed_sid, main_watchgroup ); FREESID( mainw->begin_sid, progress_get() ); FREESID( mainw->update_sid, progress_get() ); FREESID( mainw->end_sid, progress_get() ); UNREF( mainw->kitgview ); /* We don't unref wsg: it's destroyed by mainw_popdown() with * filemodel_inter_savenclose_cb(). */ mainw_all = g_slist_remove( mainw_all, mainw ); G_OBJECT_CLASS( parent_class )->dispose( object ); } static void * mainw_configure_event_sub( Workspace *ws, GdkEventConfigure *event ) { MODEL( ws )->window_x = event->x; MODEL( ws )->window_y = event->y; MODEL( ws )->window_width = event->width; MODEL( ws )->window_height = event->height; return( NULL ); } static gboolean mainw_configure_event( GtkWidget *widget, GdkEventConfigure *event ) { Mainw *mainw = MAINW( widget ); /* We have to record on all wses, since we don't know which will be * first on reload. */ workspacegroup_map( mainw->wsg, (workspace_map_fn) mainw_configure_event_sub, event, NULL ); return( GTK_WIDGET_CLASS( parent_class )-> configure_event( widget, event ) ); } static void mainw_class_init( MainwClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); GtkWidgetClass *widget_class = (GtkWidgetClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = mainw_finalize; gobject_class->dispose = mainw_dispose; widget_class->configure_event = mainw_configure_event; } static void mainw_progress_begin( Progress *progress, Mainw *mainw ) { mainw->cancel = FALSE; gtk_widget_show( mainw->progress_box ); } static void mainw_progress_update( Progress *progress, gboolean *cancel, Mainw *mainw ) { gtk_progress_bar_set_text( GTK_PROGRESS_BAR( mainw->progress ), vips_buf_all( &progress->feedback ) ); gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR( mainw->progress ), IM_CLIP( 0.0, (double) progress->percent / 100.0, 1.0 ) ); if( mainw->cancel ) *cancel = TRUE; } static void mainw_progress_end( Progress *progress, Mainw *mainw ) { gtk_widget_hide( mainw->progress_box ); mainw->cancel = FALSE; } static void mainw_init( Mainw *mainw ) { mainw->wsg = NULL; mainw->changed_sid = 0; mainw->imageinfo_changed_sid = 0; mainw->heap_changed_sid = 0; mainw->watch_changed_sid = 0; mainw->begin_sid = g_signal_connect( progress_get(), "begin", G_CALLBACK( mainw_progress_begin ), mainw ); mainw->update_sid = g_signal_connect( progress_get(), "update", G_CALLBACK( mainw_progress_update ), mainw ); mainw->end_sid = g_signal_connect( progress_get(), "end", G_CALLBACK( mainw_progress_end ), mainw ); mainw->cancel = FALSE; mainw->free_type = FALSE; mainw->toolbar_visible = MAINW_TOOLBAR; mainw->statusbar_visible = MAINW_STATUSBAR; mainw->kitgview = NULL; mainw->toolbar = NULL; mainw->statusbar_main = NULL; mainw->statusbar = NULL; mainw->space_free = NULL; mainw->space_free_eb = NULL; mainw->progress_box = NULL; mainw->progress = NULL; mainw_all = g_slist_prepend( mainw_all, mainw ); } GType mainw_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( MainwClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) mainw_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Mainw ), 32, /* n_preallocs */ (GInstanceInitFunc) mainw_init, }; type = g_type_register_static( TYPE_IWINDOW, "Mainw", &info, 0 ); } return( type ); } static void mainw_cancel_cb( GtkWidget *wid, Mainw *mainw ) { mainw->cancel = TRUE; } void mainw_find_disc( VipsBuf *buf ) { double sz = find_space( PATH_TMP ); if( sz < 0 ) vips_buf_appendf( buf, _( "No temp area" ) ); else { char txt[MAX_STRSIZE]; VipsBuf buf2 = VIPS_BUF_STATIC( txt ); vips_buf_append_size( &buf2, sz ); vips_buf_appendf( buf, _( "%s free" ), vips_buf_all( &buf2 ) ); } } void mainw_find_heap( VipsBuf *buf, Heap *heap ) { /* How much we can still expand the heap by ... this * can be -ve if we've closed a workspace, or changed * the upper limit. */ int togo = IM_MAX( 0, (heap->mxb - heap->nb) * heap->rsz ); vips_buf_appendf( buf, _( "%d cells free" ), heap->nfree + togo ); } Workspace * mainw_get_workspace( Mainw *mainw ) { Workspace *ws; if( mainw->wsg && (ws = WORKSPACE( ICONTAINER( mainw->wsg )->current )) ) return( ws ); return( NULL ); } Workspace * mainw_next_workspace( Mainw *mainw ) { Workspace *ws; if( mainw->wsg && (ws = WORKSPACE( icontainer_next( ICONTAINER( mainw->wsg ) ) )) ) return( ws ); return( NULL ); } /* Update the space remaining indicator. */ static void mainw_free_update( Mainw *mainw ) { Heap *heap = reduce_context->heap; char txt[80]; VipsBuf buf = VIPS_BUF_STATIC( txt ); Workspace *ws; if( (ws = mainw_get_workspace( mainw )) && workspace_selected_any( ws ) ) { vips_buf_appends( &buf, _( "Selected:" ) ); vips_buf_appends( &buf, " " ); workspace_selected_names( ws, &buf, ", " ); } else { /* Out of space? Make sure we swap to cell display. */ if( !heap->free ) mainw->free_type = FALSE; if( mainw->free_type ) mainw_find_heap( &buf, heap ); else mainw_find_disc( &buf ); } set_glabel( mainw->space_free, "%s", vips_buf_all( &buf ) ); } static void mainw_title_update( Mainw *mainw ) { Workspace *ws; char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char *filename; if( mainw->wsg && FILEMODEL( mainw->wsg )->modified ) vips_buf_appendf( &buf, "*" ); if( mainw->wsg && (filename = FILEMODEL( mainw->wsg )->filename) ) { char *base = g_path_get_basename( filename ); char *dir = g_path_get_dirname( filename ); vips_buf_appendf( &buf, "%s (%s)", base, dir ); g_free( base ); g_free( dir ); } else vips_buf_appends( &buf, _( "unsaved workspace" ) ); if( (ws = mainw_get_workspace( mainw )) ) { vips_buf_appends( &buf, " - " ); vips_buf_appendf( &buf, "%s", NN( IOBJECT( ws->sym )->name ) ); if( ws->compat_major ) { vips_buf_appends( &buf, " - " ); vips_buf_appends( &buf, _( "compatibility mode" ) ); vips_buf_appendf( &buf, " %d.%d", ws->compat_major, ws->compat_minor ); } } vips_buf_appendf( &buf, " - %s", PACKAGE ); iwindow_set_title( IWINDOW( mainw ), "%s", vips_buf_all( &buf ) ); } static void mainw_status_update( Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) && ws->status ) gtk_label_set_text( GTK_LABEL( mainw->statusbar ), ws->status ); else { char txt[256]; im_snprintf( txt, 256, _( NIP_COPYRIGHT ), PACKAGE ); gtk_label_set_markup( GTK_LABEL( mainw->statusbar ), txt ); } } static gboolean mainw_refresh_timeout_cb( gpointer user_data ) { static GtkToolbarStyle styles[] = { 0, /* Overwrite with system default */ GTK_TOOLBAR_ICONS, GTK_TOOLBAR_TEXT, GTK_TOOLBAR_BOTH, GTK_TOOLBAR_BOTH_HORIZ }; static gboolean inited_default_style = FALSE; /* Keep in step with the WorkspaceMode enum. */ const static char *view_mode[] = { "Normal", "ShowFormula", "NoEdit" }; Mainw *mainw = MAINW( user_data ); iWindow *iwnd = IWINDOW( mainw ); int pref = IM_CLIP( 0, MAINW_TOOLBAR_STYLE, IM_NUMBER( styles ) - 1 ); GtkAction *action; Workspace *ws; #ifdef DEBUG printf( "mainw_refresh_timeout_cb: %p\n", mainw ); #endif /*DEBUG*/ mainw->refresh_timeout = 0; mainw_status_update( mainw ); mainw_free_update( mainw ); mainw_title_update( mainw ); if( !inited_default_style ) { styles[0] = gtk_toolbar_get_style( GTK_TOOLBAR( mainw->toolbar ) ); inited_default_style = TRUE; } gtk_toolbar_set_style( GTK_TOOLBAR( mainw->toolbar ), styles[pref] ); action = gtk_action_group_get_action( iwnd->action_group, "AutoRecalculate" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), mainw_auto_recalc ); action = gtk_action_group_get_action( iwnd->action_group, "Toolbar" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), mainw->toolbar_visible ); widget_visible( mainw->toolbar, mainw->toolbar_visible ); action = gtk_action_group_get_action( iwnd->action_group, "Statusbar" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), mainw->statusbar_visible ); widget_visible( mainw->statusbar_main, mainw->statusbar_visible ); if( (ws = mainw_get_workspace( mainw )) ) { action = gtk_action_group_get_action( iwnd->action_group, "Lock" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), ws->locked ); action = gtk_action_group_get_action( iwnd->action_group, "Tabdefs" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), ws->lpane_open ); action = gtk_action_group_get_action( iwnd->action_group, "ToolkitBrowser" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), ws->rpane_open ); action = gtk_action_group_get_action( iwnd->action_group, view_mode[ws->mode] ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), TRUE ); workspace_jump_update( ws, mainw->jump_to_column_menu ); if( mainw->kitg != ws->kitg ) { UNREF( mainw->kitgview ); mainw->kitgview = TOOLKITGROUPVIEW( model_view_new( MODEL( ws->kitg ), NULL ) ); g_object_ref( G_OBJECT( mainw->kitgview ) ); gtk_object_sink( GTK_OBJECT( mainw->kitgview ) ); toolkitgroupview_set_mainw( mainw->kitgview, mainw ); gtk_menu_set_accel_group( GTK_MENU( mainw->kitgview->menu ), iwnd->accel_group ); mainw->kitg = ws->kitg; } } return( FALSE ); } static void mainw_refresh( Mainw *mainw ) { IM_FREEF( g_source_remove, mainw->refresh_timeout ); mainw->refresh_timeout = g_timeout_add( 100, (GSourceFunc) mainw_refresh_timeout_cb, mainw ); } static void mainw_duplicate_action_cb( GtkAction *action, Mainw *mainw ) { Workspacegroup *new_wsg; Mainw *new_mainw; progress_begin(); if( !(new_wsg = workspacegroup_duplicate( mainw->wsg )) ) { progress_end(); iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR ); return; } new_mainw = mainw_new( new_wsg ); gtk_widget_show( GTK_WIDGET( new_mainw ) ); symbol_recalculate_all(); progress_end(); } static void mainw_save_action_cb( GtkAction *action, Mainw *mainw ) { workspacegroup_set_save_type( mainw->wsg, WORKSPACEGROUP_SAVE_ALL ); filemodel_inter_save( IWINDOW( mainw ), FILEMODEL( mainw->wsg ) ); } static void mainw_save_as_action_cb( GtkAction *action, Mainw *mainw ) { workspacegroup_set_save_type( mainw->wsg, WORKSPACEGROUP_SAVE_ALL ); filemodel_inter_saveas( IWINDOW( mainw ), FILEMODEL( mainw->wsg ) ); } /* Event in the "space free" display ... toggle mode on left click. */ static gint mainw_space_free_event( GtkWidget *widget, GdkEvent *ev, Mainw *mainw ) { if( ev->type == GDK_BUTTON_RELEASE ) { mainw->free_type = !mainw->free_type; mainw_free_update( mainw ); } return( FALSE ); } /* Count number and sizes of all image objects. */ static void * mainw_count_images( VipsObject *object, int *n ) { if( VIPS_IS_IMAGE( object ) ) *n += 1; return( NULL ); } static void * mainw_size_images( VipsObject *object, size_t *size ) { if( VIPS_IS_IMAGE( object ) ) *size += VIPS_IMAGE_SIZEOF_IMAGE( VIPS_IMAGE( object ) ); return( NULL ); } static void mainw_space_free_tooltip_generate( GtkWidget *widget, VipsBuf *buf, Mainw *mainw ) { Heap *heap = reduce_context->heap; size_t size; int n; mainw_find_disc( buf ); /* Expands to (eg.) "14GB free in /pics/tmp" */ vips_buf_appendf( buf, _( " in \"%s\"" ), PATH_TMP ); vips_buf_appends( buf, ", " ); vips_buf_appendf( buf, _( "%d cells in heap, %d cells free, %d cells maximum" ), heap->ncells, heap->nfree, heap->max_fn( heap ) ); vips_buf_appends( buf, ", " ); if( mainw->wsg ) { vips_buf_appendf( buf, _( "%d objects in workspace" ), workspacegroup_get_n_objects( mainw->wsg ) ); vips_buf_appends( buf, ", " ); } vips_buf_appendf( buf, _( "%d vips calls cached" ), cache_history_size ); vips_buf_appends( buf, ", " ); vips_buf_appendf( buf, _( "using %d threads" ), im_concurrency_get() ); vips_buf_appends( buf, ", " ); size = 0; vips_object_map( (VipsSListMap2Fn) mainw_size_images, &size, NULL ); n = 0; vips_object_map( (VipsSListMap2Fn) mainw_count_images, &n, NULL ); vips_buf_append_size( buf, size ); vips_buf_appendf( buf, _( " in %d images" ), n ); } static void mainw_free_changed_cb( gpointer *dummy, Mainw *mainw ) { mainw_free_update( mainw ); mainw_status_update( mainw ); } /* Go to home page. */ void mainw_homepage_action_cb( GtkAction *action, iWindow *iwnd ) { box_url( GTK_WIDGET( iwnd ), VIPS_HOMEPAGE ); } /* About... box. */ void mainw_about_action_cb( GtkAction *action, iWindow *iwnd ) { box_about( GTK_WIDGET( iwnd ) ); } /* User's guide. */ void mainw_guide_action_cb( GtkAction *action, iWindow *iwnd ) { box_url( GTK_WIDGET( iwnd ), "file://" NIP_DOCPATH "/nipguide.html" ); } static void mainw_selected_duplicate_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; progress_begin(); if( (ws = mainw_get_workspace( mainw )) && !workspace_selected_duplicate( ws ) ) iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR ); progress_end(); } /* Ungroup the selected object(s), or the bottom object. */ static void mainw_ungroup_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; progress_begin(); if( (ws = mainw_get_workspace( mainw )) && !workspace_selected_ungroup( ws ) ) iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR ); progress_end(); } /* Group the selected object(s). */ void mainw_group_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) workspace_selected_group( ws ); } /* Select all objects. */ static void mainw_select_all_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) workspace_select_all( ws ); } static void mainw_find_action_cb( GtkAction *action, Mainw *mainw ) { error_top( _( "Not implemented." ) ); error_sub( _( "Find in workspace not implemented yet." ) ); iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO ); } static void mainw_find_again_action_cb( GtkAction *action, Mainw *mainw ) { error_top( _( "Not implemented." ) ); error_sub( _( "Find again in workspace not implemented yet." ) ); iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO ); } void mainw_next_error_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *first_ws; Workspace *ws; if( !(first_ws = mainw_get_workspace( mainw )) ) return; do { ws = mainw_get_workspace( mainw ); if( workspace_next_error( ws ) ) return; ws = mainw_next_workspace( mainw ); } while( ws != first_ws ); error_top( _( "No errors in workspace." ) ); error_sub( "%s", _( "There are no errors (that I can see) " "in this workspace." ) ); iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO ); } static void mainw_force_calc_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) && workspace_selected_any( ws ) ) { if( !workspace_selected_recalc( ws ) ) iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR ); } else symbol_recalculate_all_force( TRUE ); } Workspacegroup * mainw_open_workspace( Workspaceroot *wsr, const char *filename ) { Workspacegroup *wsg; Mainw *mainw; if( !(wsg = workspacegroup_new_from_file( wsr, filename, filename )) ) return( NULL ); mainw = mainw_new( wsg ); gtk_widget_show( GTK_WIDGET( mainw ) ); return( wsg ); } /* Track these during a load. */ typedef struct { Mainw *mainw; VipsBuf *buf; int nitems; } MainwLoad; /* Try to open a file. Workspace files we load immediately, other ones we * add the load to a buffer. */ static void * mainw_open_fn( Filesel *filesel, const char *filename, MainwLoad *load ) { Workspaceroot *wsr = load->mainw->wsg->wsr; if( is_file_type( &filesel_wfile_type, filename ) ) { if( !mainw_open_workspace( wsr, filename ) ) return( filesel ); mainw_recent_add( &mainw_recent_workspace, filename ); } else { if( load->nitems ) vips_buf_appends( load->buf, ", " ); if( !workspace_load_file_buf( load->buf, filename ) ) return( filesel ); mainw_recent_add( &mainw_recent_image, filename ); load->nitems += 1; } return( NULL ); } /* Callback from load browser. */ static void mainw_open_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Mainw *mainw = MAINW( client ); Filesel *filesel = FILESEL( iwnd ); char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); MainwLoad load; Workspace *ws; load.mainw = mainw; load.buf = &buf; load.nitems = 0; if( filesel_map_filename_multi( filesel, (FileselMapFn) mainw_open_fn, &load, NULL ) ) { nfn( sys, IWINDOW_ERROR ); return; } /* Some actual files (image, matrix) were selected. Load into * the current workspace. */ if( load.nitems && (ws = mainw_get_workspace( mainw )) ) { char txt2[MAX_STRSIZE]; VipsBuf buf2 = VIPS_BUF_STATIC( txt2 ); if( load.nitems > 1 ) vips_buf_appendf( &buf2, "Group [%s]", vips_buf_all( &buf ) ); else vips_buf_appends( &buf2, vips_buf_all( &buf ) ); if( !workspace_add_def_recalc( ws, vips_buf_all( &buf2 ) ) ) { error_top( _( "Load failed." ) ); error_sub( _( "Unable to execute:\n %s" ), vips_buf_all( &buf2 ) ); nfn( sys, IWINDOW_ERROR ); return; } } /* Wses will need a recalc. */ symbol_recalculate_all(); /* If we had an empty wsg, perhaps we've just started up, * kill it. */ if( workspacegroup_is_empty( mainw->wsg ) ) { filemodel_set_modified( FILEMODEL( mainw->wsg ), FALSE ); iwindow_kill( IWINDOW( mainw ) ); } nfn( sys, IWINDOW_YES ); } /* Show an open file dialog ... any type, but default to one of the image * ones. */ static void mainw_open( Mainw *mainw ) { GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Open File" ) ); filesel_set_flags( FILESEL( filesel ), TRUE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_mainw, IMAGE_FILE_TYPE ); filesel_set_filetype_pref( FILESEL( filesel ), "IMAGE_FILE_TYPE" ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( mainw ) ); filesel_set_done( FILESEL( filesel ), mainw_open_done_cb, mainw ); filesel_set_multi( FILESEL( filesel ), TRUE ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } void mainw_open_action_cb( GtkAction *action, Mainw *mainw ) { mainw_open( mainw ); } /* Open one of the example workspaces ... just a shortcut. */ static void mainw_open_examples( Mainw *mainw ) { GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Open File" ) ); filesel_set_flags( FILESEL( filesel ), TRUE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( mainw ) ); filesel_set_done( FILESEL( filesel ), mainw_open_done_cb, mainw ); filesel_set_multi( FILESEL( filesel ), TRUE ); iwindow_build( IWINDOW( filesel ) ); filesel_set_filename( FILESEL( filesel ), "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "data" G_DIR_SEPARATOR_S "examples" G_DIR_SEPARATOR_S "1_point_mosaic" ); /* Reset the filetype ... setting the filename will have changed it to * 'all', since there's no suffix. */ filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 ); gtk_widget_show( GTK_WIDGET( filesel ) ); } static void mainw_open_examples_action_cb( GtkAction *action, Mainw *mainw ) { mainw_open_examples( mainw ); } static gboolean mainw_recent_open( Mainw *mainw, const char *filename ) { Workspacegroup *wsg = mainw->wsg; if( is_file_type( &filesel_wfile_type, filename ) ) { if( !mainw_open_workspace( wsg->wsr, filename ) ) return( FALSE ); /* If we had an empty wsg, perhaps we've just started up, * kill it. */ if( workspacegroup_is_empty( wsg ) ) { filemodel_set_modified( FILEMODEL( wsg ), FALSE ); iwindow_kill( IWINDOW( mainw ) ); } } else { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) && !workspace_load_file( ws, filename ) ) return( FALSE ); } return( TRUE ); } static void mainw_recent_open_cb( GtkWidget *widget, const char *filename ) { Mainw *mainw = MAINW( iwindow_get_root( widget ) ); progress_begin(); if( !mainw_recent_open( mainw, filename ) ) { iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR ); progress_end(); return; } progress_end(); symbol_recalculate_all(); } static void mainw_recent_build( GtkWidget *menu, GSList *recent ) { GSList *p; for( p = recent; p; p = p->next ) { const char *filename = (const char *) p->data; GtkWidget *item; char txt[80]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char *utf8; vips_buf_appendf( &buf, " %s", im_skip_dir( filename ) ); utf8 = f2utf8( vips_buf_all( &buf ) ); item = gtk_menu_item_new_with_label( utf8 ); g_free( utf8 ); utf8 = f2utf8( filename ); set_tooltip( item, "%s", utf8 ); g_free( utf8 ); gtk_menu_append( GTK_MENU( menu ), item ); gtk_widget_show( item ); gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( mainw_recent_open_cb ), (char *) filename ); } } static void mainw_recent_clear_cb( GtkWidget *widget, const char *filename ) { IM_FREEF( recent_free, mainw_recent_workspace ); IM_FREEF( recent_free, mainw_recent_image ); IM_FREEF( recent_free, mainw_recent_matrix ); /* Need to remove files too to prevent merge on quit. */ (void) unlinkf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), RECENT_WORKSPACE ); (void) unlinkf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), RECENT_IMAGE ); (void) unlinkf( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), RECENT_MATRIX ); } static void mainw_recent_map_cb( GtkWidget *widget, Mainw *mainw ) { GtkWidget *menu = mainw->recent_menu; GtkWidget *item; gtk_container_foreach( GTK_CONTAINER( menu ), (GtkCallback) gtk_widget_destroy, NULL ); if( mainw_recent_image ) { item = gtk_menu_item_new_with_label( _( "Recent Images" ) ); gtk_menu_append( GTK_MENU( menu ), item ); gtk_widget_show( item ); gtk_widget_set_sensitive( item, FALSE ); mainw_recent_build( menu, mainw_recent_image ); } if( mainw_recent_workspace ) { item = gtk_menu_item_new_with_label( _( "Recent Workspaces" ) ); gtk_menu_append( GTK_MENU( menu ), item ); gtk_widget_show( item ); gtk_widget_set_sensitive( item, FALSE ); mainw_recent_build( menu, mainw_recent_workspace ); } if( mainw_recent_matrix ) { item = gtk_menu_item_new_with_label( _( "Recent Matricies" ) ); gtk_menu_append( GTK_MENU( menu ), item ); gtk_widget_show( item ); gtk_widget_set_sensitive( item, FALSE ); mainw_recent_build( menu, mainw_recent_matrix ); } if( !mainw_recent_workspace && !mainw_recent_image && !mainw_recent_matrix ) { item = gtk_menu_item_new_with_label( _( "No recent items" ) ); gtk_menu_append( GTK_MENU( menu ), item ); gtk_widget_show( item ); gtk_widget_set_sensitive( item, FALSE ); } else { item = gtk_menu_item_new_with_label( _( "Clear Recent Menu" ) ); gtk_menu_append( GTK_MENU( menu ), item ); gtk_widget_show( item ); gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( mainw_recent_clear_cb ), NULL ); } } /* Merge a .ws into this wsg. */ static void * mainw_workspace_merge_fn( Filesel *filesel, const char *filename, void *a, void *b ) { Mainw *mainw = MAINW( a ); if( !workspacegroup_merge_workspaces( mainw->wsg, filename ) ) return( filesel ); /* Process some events to make sure we rethink the layout and * are able to get the append-at-RHS offsets. */ process_events(); symbol_recalculate_all(); mainw_recent_add( &mainw_recent_workspace, filename ); return( NULL ); } /* Callback from load browser. */ static void mainw_workspace_merge_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Mainw *mainw = MAINW( client ); if( filesel_map_filename_multi( filesel, mainw_workspace_merge_fn, mainw, NULL ) ) { symbol_recalculate_all(); nfn( sys, IWINDOW_ERROR ); return; } symbol_recalculate_all(); nfn( sys, IWINDOW_YES ); } /* Merge ws file into current ws. */ void mainw_workspace_merge( Mainw *mainw ) { GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Merge Workspace from File" ) ); filesel_set_flags( FILESEL( filesel ), FALSE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( mainw ) ); filesel_set_done( FILESEL( filesel ), mainw_workspace_merge_done_cb, mainw ); filesel_set_multi( FILESEL( filesel ), TRUE ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } void mainw_workspace_merge_action_cb( GtkAction *action, Mainw *mainw ) { mainw_workspace_merge( mainw ); } /* Load a workspace, called from a yesno dialog. */ static void mainw_auto_recover_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { char *filename = (char *) client; Workspacegroup *new_wsg; Mainw *new_mainw; progress_begin(); if( !(new_wsg = workspacegroup_new_from_file( main_workspaceroot, filename, filename )) ) { progress_end(); nfn( sys, IWINDOW_ERROR ); return; } filemodel_set_filename( FILEMODEL( new_wsg ), NULL ); new_mainw = mainw_new( new_wsg ); gtk_widget_show( GTK_WIDGET( new_mainw ) ); symbol_recalculate_all(); progress_end(); nfn( sys, IWINDOW_YES ); } /* Auto recover. */ static void mainw_recover_action_cb( GtkAction *action, Mainw *mainw ) { char *filename; if( !(filename = workspacegroup_autosave_recover()) ) { if( !AUTO_WS_SAVE ) { error_top( _( "No backup workspaces found." ) ); error_sub( "%s", _( "You need to enable \"Auto workspace " "save\" in Preferences " "before automatic recovery works." ) ); } else { error_top( _( "No backup workspaces found." ) ); error_sub( _( "No suitable workspace save files found " "in \"%s\"" ), PATH_TMP ); } iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO ); return; } /* Tricksy ... free str with notify callack from yesno. */ box_yesno( GTK_WIDGET( mainw ), mainw_auto_recover_cb, iwindow_true_cb, filename, (iWindowNotifyFn) im_free, filename, GTK_STOCK_OPEN, _( "Open workspace backup?" ), _( "Found workspace backup:\n\n\t%s\n\n" "Do you want to recover this workspace?" ), filename ); } /* Callback from make new column. */ void mainw_column_new_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) (void) workspace_column_new( ws ); } /* Done button hit. */ static void mainw_column_new_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Mainw *mainw = MAINW( client ); Stringset *ss = STRINGSET( iwnd ); StringsetChild *name = stringset_child_get( ss, _( "Name" ) ); StringsetChild *caption = stringset_child_get( ss, _( "Caption" ) ); Workspace *ws; Column *col; char name_text[1024]; char caption_text[1024]; if( !(ws = mainw_get_workspace( mainw )) ) { nfn( sys, IWINDOW_YES ); return; } if( !get_geditable_name( name->entry, name_text, 1024 ) || !get_geditable_string( caption->entry, caption_text, 1024 ) ) { nfn( sys, IWINDOW_ERROR ); return; } if( !(col = column_new( ws, name_text )) ) { nfn( sys, IWINDOW_ERROR ); return; } if( strcmp( caption_text, "" ) != 0 ) iobject_set( IOBJECT( col ), NULL, caption_text ); workspace_column_select( ws, col ); column_scrollto( col, MODEL_SCROLL_TOP ); nfn( sys, IWINDOW_YES ); } /* Make a new column with a specified name. */ static void mainw_column_new_named_action_cb( GtkAction *action, Mainw *mainw ) { GtkWidget *ss = stringset_new(); char new_name[MAX_STRSIZE]; Workspace *ws; if( !(ws = mainw_get_workspace( mainw )) ) return; workspace_column_name_new( ws, new_name ); stringset_child_new( STRINGSET( ss ), _( "Name" ), new_name, _( "Set column name here" ) ); stringset_child_new( STRINGSET( ss ), _( "Caption" ), "", _( "Set column caption here" ) ); iwindow_set_title( IWINDOW( ss ), _( "New Column" ) ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, mainw ); idialog_add_ok( IDIALOG( ss ), mainw_column_new_cb, _( "Create Column" ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( mainw ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } /* Callback from program. */ static void mainw_program_new_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) { Program *program; program = program_new( ws->kitg ); gtk_widget_show( GTK_WIDGET( program ) ); } } static void mainw_workspace_new_action_cb( GtkAction *action, Mainw *mainw ) { workspace_new_blank( mainw->wsg ); } /* New workbook. */ static void mainw_workbook_new_action_cb( GtkAction *action, Mainw *mainw ) { Mainw *new_mainw; Workspacegroup *new_wsg; char name[256]; workspaceroot_name_new( mainw->wsg->wsr, name ); new_wsg = workspacegroup_new_blank( mainw->wsg->wsr, name ); new_mainw = mainw_new( new_wsg ); gtk_widget_show( GTK_WIDGET( new_mainw ) ); } /* Callback from auto-recalc toggle. */ static void mainw_autorecalc_action_cb( GtkToggleAction *action, Mainw *mainw ) { GSList *i; mainw_auto_recalc = gtk_toggle_action_get_active( action ); /* Yuk! We have to ask all mainw to refresh by hand, since we're not * using the prefs system for auto_recalc for reasons noted at top. */ for( i = mainw_all; i; i = i->next ) mainw_refresh( MAINW( i->data ) ); if( mainw_auto_recalc ) symbol_recalculate_all(); } /* Callback from lock toggle. */ static void mainw_lock_action_cb( GtkToggleAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) workspace_set_locked( ws, gtk_toggle_action_get_active( action ) ); } /* Callback from show toolbar toggle. */ static void mainw_toolbar_action_cb( GtkToggleAction *action, Mainw *mainw ) { mainw->toolbar_visible = gtk_toggle_action_get_active( action ); prefs_set( "MAINW_TOOLBAR", "%s", bool_to_char( mainw->toolbar_visible ) ); mainw_refresh( mainw ); } /* Callback from show statusbar toggle. */ static void mainw_statusbar_action_cb( GtkToggleAction *action, Mainw *mainw ) { mainw->statusbar_visible = gtk_toggle_action_get_active( action ); prefs_set( "MAINW_STATUSBAR", "%s", bool_to_char( mainw->statusbar_visible ) ); mainw_refresh( mainw ); } /* Expose/hide the toolkit browser. */ static void mainw_toolkitbrowser_action_cb( GtkToggleAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) { ws->rpane_open = gtk_toggle_action_get_active( action ); iobject_changed( IOBJECT( ws ) ); } } /* Expose/hide the workspace defs. */ static void mainw_tabdefs_action_cb( GtkToggleAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) { ws->lpane_open = gtk_toggle_action_get_active( action ); iobject_changed( IOBJECT( ws ) ); } } /* Remove selected items. */ static void mainw_selected_remove_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) workspace_selected_remove_yesno( ws, GTK_WIDGET( mainw ) ); } void mainw_revert_ok_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Prefs *prefs = PREFS( client ); Workspacegroup *wsg = workspace_get_workspacegroup( prefs->ws ); if( FILEMODEL( wsg )->filename ) { (void) unlinkf( "%s", FILEMODEL( wsg )->filename ); main_reload(); symbol_recalculate_all(); } nfn( sys, IWINDOW_YES ); } void mainw_revert_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Prefs *prefs = PREFS( client ); box_yesno( GTK_WIDGET( iwnd ), mainw_revert_ok_cb, iwindow_true_cb, prefs, nfn, sys, _( "Revert to Defaults" ), _( "Revert to installation defaults?" ), _( "Would you like to reset all preferences to their factory " "settings? This will delete any changes you have ever made " "to your preferences and may take a few seconds." ) ); } static void mainw_preferences_action_cb( GtkAction *action, Mainw *mainw ) { Prefs *prefs; /* Can fail if there's no prefs ws, or an error on load. */ if( !(prefs = prefs_new( NULL )) ) { iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR ); return; } iwindow_set_title( IWINDOW( prefs ), _( "Preferences" ) ); iwindow_set_parent( IWINDOW( prefs ), GTK_WIDGET( mainw ) ); idialog_set_callbacks( IDIALOG( prefs ), NULL, NULL, NULL, prefs ); idialog_add_ok( IDIALOG( prefs ), mainw_revert_cb, _( "Revert to Defaults ..." ) ); idialog_add_ok( IDIALOG( prefs ), iwindow_true_cb, GTK_STOCK_CLOSE ); iwindow_build( IWINDOW( prefs ) ); /* Just big enough to avoid a horizontal scrollbar on my machine. FIXME ... Yuk! There must be a better way to do this! Maybe a setting in prefs to suppress the h scrollbar? */ gtk_window_set_default_size( GTK_WINDOW( prefs ), 780, 480 ); gtk_widget_show( GTK_WIDGET( prefs ) ); } /* Make a magic definition for the selected symbol. FIXME .. paste this back when magic is reinstated static void mainw_magic_cb( gpointer callback_data, guint callback_action, GtkWidget *widget ) { Workspace *ws = main_workspaceroot->current; Row *row = workspace_selected_one( ws ); if( !row ) box_alert( mainw, "Select a single object with left-click, " "select Magic Definition." ); else if( !magic_sym( row->sym ) ) iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR ); else workspace_deselect_all( ws ); } */ #ifdef HAVE_LIBGVC static void mainw_graph_action_cb( GtkAction *action, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) { Graphwindow *graphwindow; graphwindow = graphwindow_new( ws, GTK_WIDGET( mainw ) ); gtk_widget_show( GTK_WIDGET( graphwindow ) ); } } #endif /*HAVE_LIBGVC*/ /* Set display mode. */ static void mainw_mode_action_cb( GtkRadioAction *action, GtkRadioAction *current, Mainw *mainw ) { Workspace *ws; if( (ws = mainw_get_workspace( mainw )) ) workspace_set_mode( ws, gtk_radio_action_get_current_value( action ) ); } /* Our actions. */ static GtkActionEntry mainw_actions[] = { /* Menu items. */ { "RecentMenu", NULL, N_( "Open _Recent" ) }, { "JumpToColumnMenu", NULL, N_( "Jump to _Column" ) }, { "ToolkitsMenu", NULL, N_( "_Toolkits" ) }, /* Dummy action ... replaced at runtime. */ { "Stub", NULL, "", NULL, "", NULL }, /* Actions. */ { "NewColumn", GTK_STOCK_NEW, N_( "C_olumn" ), NULL, N_( "Create a new column" ), G_CALLBACK( mainw_column_new_action_cb ) }, { "NewColumnName", GTK_STOCK_NEW, N_( "C_olumn" ), NULL, N_( "Create a new column with a specified name" ), G_CALLBACK( mainw_column_new_named_action_cb ) }, { "NewTab", GTK_STOCK_NEW, N_( "_Tab" ), "T", N_( "Create a new tab" ), G_CALLBACK( mainw_workspace_new_action_cb ) }, { "NewWorkspace", GTK_STOCK_NEW, N_( "_Workspace" ), NULL, N_( "Create a new workspace" ), G_CALLBACK( mainw_workbook_new_action_cb ) }, { "Open", GTK_STOCK_OPEN, N_( "_Open" ), NULL, N_( "Open a file" ), G_CALLBACK( mainw_open_action_cb ) }, { "OpenExamples", NULL, N_( "Open _Examples" ), NULL, N_( "Open example workspaces" ), G_CALLBACK( mainw_open_examples_action_cb ) }, { "DuplicateWorkspace", STOCK_DUPLICATE, N_( "_Duplicate Workspace" ), NULL, N_( "Duplicate workspace" ), G_CALLBACK( mainw_duplicate_action_cb ) }, { "Merge", NULL, N_( "_Merge Into Workspace" ), NULL, N_( "Merge workspace into this workspace" ), G_CALLBACK( mainw_workspace_merge_action_cb ) }, { "Save", GTK_STOCK_SAVE, N_( "_Save Workspace" ), NULL, N_( "Save workspace" ), G_CALLBACK( mainw_save_action_cb ) }, { "SaveAs", GTK_STOCK_SAVE_AS, N_( "_Save Workspace As" ), NULL, N_( "Save workspace as" ), G_CALLBACK( mainw_save_as_action_cb ) }, { "Recover", NULL, N_( "Search for Workspace _Backups" ), NULL, N_( "Load last automatically backed-up workspace" ), G_CALLBACK( mainw_recover_action_cb ) }, { "Delete", GTK_STOCK_DELETE, N_( "_Delete" ), "BackSpace", N_( "Delete selected items" ), G_CALLBACK( mainw_selected_remove_action_cb ) }, { "SelectAll", NULL, N_( "Select _All" ), "A", N_( "Select all items" ), G_CALLBACK( mainw_select_all_action_cb ) }, { "Duplicate", STOCK_DUPLICATE, N_( "D_uplicate Selected" ), "U", N_( "Duplicate selected items" ), G_CALLBACK( mainw_selected_duplicate_action_cb ) }, { "Recalculate", NULL, N_( "_Recalculate" ), NULL, N_( "Recalculate selected items" ), G_CALLBACK( mainw_force_calc_action_cb ) }, { "Find", GTK_STOCK_FIND, N_( "_Find" ), NULL, N_( "Find in workspace" ), G_CALLBACK( mainw_find_action_cb ) }, { "FindNext", NULL, N_( "Find _Next" ), NULL, N_( "Find again in workspace" ), G_CALLBACK( mainw_find_again_action_cb ) }, { "NextError", STOCK_NEXT_ERROR, NULL, NULL, N_( "Jump to next error" ), G_CALLBACK( mainw_next_error_action_cb ) }, { "Group", NULL, N_( "_Group" ), NULL, N_( "Group selected items" ), G_CALLBACK( mainw_group_action_cb ) }, { "Ungroup", NULL, N_( "U_ngroup" ), NULL, N_( "Ungroup selected items" ), G_CALLBACK( mainw_ungroup_action_cb ) }, { "Preferences", GTK_STOCK_PREFERENCES, N_( "_Preferences" ), NULL, N_( "Edit preferences" ), G_CALLBACK( mainw_preferences_action_cb ) }, #ifdef HAVE_LIBGVC { "Graph", NULL, N_( "Workspace as Grap_h" ), NULL, N_( "Show a graph of workspace dependencies" ), G_CALLBACK( mainw_graph_action_cb ) }, #endif /*HAVE_LIBGVC*/ { "EditToolkits", NULL, N_( "_Edit" ), NULL, N_( "Edit toolkits" ), G_CALLBACK( mainw_program_new_action_cb ) } }; static GtkToggleActionEntry mainw_toggle_actions[] = { { "AutoRecalculate", NULL, N_( "Au_to Recalculate" ), NULL, N_( "Recalculate automatically on change" ), G_CALLBACK( mainw_autorecalc_action_cb ), TRUE }, { "Lock", NULL, N_( "_Lock tab" ), NULL, N_( "Lock tab" ), G_CALLBACK( mainw_lock_action_cb ), TRUE }, { "Toolbar", NULL, N_( "_Toolbar" ), NULL, N_( "Show window toolbar" ), G_CALLBACK( mainw_toolbar_action_cb ), TRUE }, { "Statusbar", NULL, N_( "_Statusbar" ), NULL, N_( "Show window statusbar" ), G_CALLBACK( mainw_statusbar_action_cb ), TRUE }, { "ToolkitBrowser", NULL, N_( "Toolkit _Browser" ), NULL, N_( "Show toolkit browser" ), G_CALLBACK( mainw_toolkitbrowser_action_cb ), FALSE }, { "Tabdefs", NULL, N_( "Tab _Definitions" ), NULL, N_( "Show tab definitions" ), G_CALLBACK( mainw_tabdefs_action_cb ), FALSE }, }; static GtkRadioActionEntry mainw_radio_actions[] = { { "Normal", NULL, N_( "_Normal" ), NULL, N_( "Normal view mode" ), WORKSPACE_MODE_REGULAR }, { "ShowFormula", NULL, N_( "Show _Formula" ), NULL, N_( "Show formula view mode" ), WORKSPACE_MODE_FORMULA }, { "NoEdit", NULL, N_( "No _Edits" ), NULL, N_( "No edits view mode" ), WORKSPACE_MODE_NOEDIT }, }; static const char *mainw_menubar_ui_description = "" " " " " " " " " " " " " " " " " " " " " /* Dummy ... replaced on map */ " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " /* Dummy ... replaced on map */ " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef HAVE_LIBGVC " " " " #endif /*HAVE_LIBGVC*/ " " " " " " " " " " " " " " " " /* Toolkits pasted here at runtime */ " " " " " " " " " " " " " " " " ""; static const char *mainw_toolbar_ui_description = "" " " " " " " " " " " " " " " " " " " ""; static void mainw_watch_changed_cb( Watchgroup *watchgroup, Watch *watch, Mainw *mainw ) { if( strcmp( IOBJECT( watch )->name, "MAINW_TOOLBAR_STYLE" ) == 0 ) mainw->toolbar_visible = MAINW_TOOLBAR; mainw_refresh( mainw ); } /* Make the insides of the panel. */ static void mainw_build( iWindow *iwnd, GtkWidget *vbox ) { Mainw *mainw = MAINW( iwnd ); GtkWidget *mbar; GtkWidget *frame; GError *error; GtkWidget *cancel; GtkWidget *item; #ifdef DEBUG printf( "mainw_build: %p\n", mainw ); #endif /*DEBUG*/ /* Make main menu bar */ gtk_action_group_add_actions( iwnd->action_group, mainw_actions, G_N_ELEMENTS( mainw_actions ), GTK_WINDOW( mainw ) ); gtk_action_group_add_toggle_actions( iwnd->action_group, mainw_toggle_actions, G_N_ELEMENTS( mainw_toggle_actions ), GTK_WINDOW( mainw ) ); gtk_action_group_add_radio_actions( iwnd->action_group, mainw_radio_actions, G_N_ELEMENTS( mainw_radio_actions ), WORKSPACE_MODE_REGULAR, G_CALLBACK( mainw_mode_action_cb ), GTK_WINDOW( mainw ) ); error = NULL; if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager, mainw_menubar_ui_description, -1, &error ) || !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager, mainw_toolbar_ui_description, -1, &error ) ) { g_message( "building menus failed: %s", error->message ); g_error_free( error ); exit( EXIT_FAILURE ); } mbar = gtk_ui_manager_get_widget( iwnd->ui_manager, "/MainwMenubar" ); gtk_box_pack_start( GTK_BOX( vbox ), mbar, FALSE, FALSE, 0 ); gtk_widget_show( mbar ); /* Get the dummy item on the recent menu, then get the enclosing menu * for that dummy item. */ item = gtk_ui_manager_get_widget( iwnd->ui_manager, "/MainwMenubar/FileMenu/RecentMenu/Stub" ); mainw->recent_menu = gtk_widget_get_parent( GTK_WIDGET( item ) ); gtk_signal_connect( GTK_OBJECT( mainw->recent_menu ), "map", GTK_SIGNAL_FUNC( mainw_recent_map_cb ), mainw ); /* Same for the column jump menu. */ item = gtk_ui_manager_get_widget( iwnd->ui_manager, "/MainwMenubar/EditMenu/JumpToColumnMenu/Stub" ); mainw->jump_to_column_menu = gtk_widget_get_parent( GTK_WIDGET( item ) ); /* Same for the tk menu. */ item = gtk_ui_manager_get_widget( iwnd->ui_manager, "/MainwMenubar/ToolkitsMenu/Stub" ); mainw->toolkit_menu = gtk_widget_get_parent( GTK_WIDGET( item ) ); gtk_widget_destroy( item ); /* Attach toolbar. */ mainw->toolbar = gtk_ui_manager_get_widget( iwnd->ui_manager, "/MainwToolbar" ); gtk_box_pack_start( GTK_BOX( vbox ), mainw->toolbar, FALSE, FALSE, 0 ); widget_visible( mainw->toolbar, MAINW_TOOLBAR ); /* This will set to NULL if we don't have infobar support. */ if( (IWINDOW( mainw )->infobar = infobar_new()) ) gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( IWINDOW( mainw )->infobar ), FALSE, FALSE, 0 ); /* hbox for status bar etc. */ mainw->statusbar_main = gtk_hbox_new( FALSE, 2 ); gtk_box_pack_end( GTK_BOX( vbox ), mainw->statusbar_main, FALSE, FALSE, 2 ); widget_visible( mainw->statusbar_main, MAINW_STATUSBAR ); /* Make space free label. */ mainw->space_free_eb = gtk_event_box_new(); gtk_box_pack_start( GTK_BOX( mainw->statusbar_main ), mainw->space_free_eb, FALSE, FALSE, 0 ); gtk_widget_show( mainw->space_free_eb ); frame = gtk_frame_new( NULL ); gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_IN ); gtk_container_add( GTK_CONTAINER( mainw->space_free_eb ), frame ); gtk_widget_show( frame ); mainw->space_free = gtk_label_new( "space_free" ); gtk_misc_set_padding( GTK_MISC( mainw->space_free ), 2, 2 ); gtk_container_add( GTK_CONTAINER( frame ), mainw->space_free ); gtk_signal_connect( GTK_OBJECT( mainw->space_free_eb ), "event", GTK_SIGNAL_FUNC( mainw_space_free_event ), mainw ); set_tooltip_generate( mainw->space_free_eb, (TooltipGenerateFn) mainw_space_free_tooltip_generate, mainw, NULL ); gtk_widget_show( mainw->space_free ); mainw->imageinfo_changed_sid = g_signal_connect( main_imageinfogroup, "changed", G_CALLBACK( mainw_free_changed_cb ), mainw ); mainw->heap_changed_sid = g_signal_connect( reduce_context->heap, "changed", G_CALLBACK( mainw_free_changed_cb ), mainw ); /* Make message label. */ mainw->statusbar = gtk_label_new( "" ); gtk_label_set_ellipsize( GTK_LABEL( mainw->statusbar ), PANGO_ELLIPSIZE_MIDDLE ); /* 6 is enough to stop the statusbar changing height when the progress * indicator changes visibility. */ gtk_misc_set_padding( GTK_MISC( mainw->statusbar ), 2, 6 ); gtk_misc_set_alignment( GTK_MISC( mainw->statusbar ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( mainw->statusbar_main ), mainw->statusbar, TRUE, TRUE, 0 ); gtk_widget_show( mainw->statusbar ); mainw->progress_box = gtk_hbox_new( FALSE, 2 ); mainw->progress = gtk_progress_bar_new(); gtk_widget_set_size_request( GTK_WIDGET( mainw->progress ), 200, -1 ); gtk_box_pack_end( GTK_BOX( mainw->progress_box ), mainw->progress, FALSE, TRUE, 0 ); gtk_widget_show( mainw->progress ); cancel = gtk_button_new_with_label( "Cancel" ); g_signal_connect( cancel, "clicked", G_CALLBACK( mainw_cancel_cb ), mainw ); gtk_box_pack_end( GTK_BOX( mainw->progress_box ), cancel, FALSE, TRUE, 0 ); gtk_widget_show( cancel ); gtk_box_pack_end( GTK_BOX( mainw->statusbar_main ), mainw->progress_box, FALSE, TRUE, 0 ); mainw->wsgview = WORKSPACEGROUPVIEW( workspacegroupview_new() ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( mainw->wsgview ), TRUE, TRUE, 0 ); view_link( VIEW( mainw->wsgview ), MODEL( mainw->wsg ), NULL ); gtk_widget_show( GTK_WIDGET( mainw->wsgview ) ); /* Any changes to prefs, refresh (yuk!). */ mainw->watch_changed_sid = g_signal_connect( main_watchgroup, "watch_changed", G_CALLBACK( mainw_watch_changed_cb ), mainw ); } static void mainw_popdown( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Mainw *mainw = MAINW( iwnd ); /* We can be destroyed in two ways: either our iwnd tells us to go, or * our model is destroyed under us. If the model has gone, we just go. * If the model is still there, we need to ask about saving and * quitting. */ if( mainw->wsg ) filemodel_inter_savenclose_cb( IWINDOW( mainw ), FILEMODEL( mainw->wsg ), nfn, sys ); else nfn( sys, IWINDOW_YES ); } static void mainw_wsg_changed_cb( Workspacegroup *wsg, Mainw *mainw ) { mainw_refresh( mainw ); } static void mainw_wsg_destroy_cb( Workspacegroup *wsg, Mainw *mainw ) { mainw->wsg = NULL; } static void mainw_link( Mainw *mainw, Workspacegroup *wsg ) { Workspace *ws = workspacegroup_get_workspace( wsg ); /* Take ownership of the wsg. */ mainw->wsg = wsg; g_object_ref( G_OBJECT( mainw->wsg ) ); iobject_sink( IOBJECT( mainw->wsg ) ); wsg->iwnd = IWINDOW( mainw ); iwindow_set_build( IWINDOW( mainw ), (iWindowBuildFn) mainw_build, wsg, NULL, NULL ); iwindow_set_popdown( IWINDOW( mainw ), mainw_popdown, NULL ); iwindow_set_size_prefs( IWINDOW( mainw ), "MAINW_WINDOW_WIDTH", "MAINW_WINDOW_HEIGHT" ); iwindow_build( IWINDOW( mainw ) ); if( ws && MODEL( ws )->window_width != - 1 ) gtk_window_set_default_size( GTK_WINDOW( mainw ), MODEL( ws )->window_width, MODEL( ws )->window_height ); /* Set start state. */ (void) mainw_refresh( mainw ); mainw->changed_sid = g_signal_connect( mainw->wsg, "changed", G_CALLBACK( mainw_wsg_changed_cb ), mainw ); mainw->destroy_sid = g_signal_connect( mainw->wsg, "destroy", G_CALLBACK( mainw_wsg_destroy_cb ), mainw ); } Mainw * mainw_new( Workspacegroup *wsg ) { Mainw *mainw; mainw = MAINW( g_object_new( TYPE_MAINW, NULL ) ); mainw_link( mainw, wsg ); return( mainw ); } static void * mainw_cull_sub( Mainw *mainw ) { if( !ICONTAINER( mainw->wsg )->children ) { filemodel_set_modified( FILEMODEL( mainw->wsg ), FALSE ); iwindow_kill( IWINDOW( mainw ) ); } return( NULL ); } void mainw_cull( void ) { slist_map( mainw_all, (SListMapFn) mainw_cull_sub, NULL ); } static void * mainw_layout_sub( Workspace *ws ) { model_layout( MODEL( ws ) ); workspace_set_needs_layout( ws, FALSE ); return( NULL ); } static gboolean mainw_layout_timeout_cb( gpointer user_data ) { mainw_layout_timeout = 0; slist_map( workspace_get_needs_layout(), (SListMapFn) mainw_layout_sub, NULL ); return( FALSE ); } void mainw_layout( void ) { IM_FREEF( g_source_remove, mainw_layout_timeout ); mainw_layout_timeout = g_timeout_add( 300, (GSourceFunc) mainw_layout_timeout_cb, NULL ); } ================================================ FILE: src/mainw.h ================================================ /* A top level window holding some workspaces */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MAINW (mainw_get_type()) #define MAINW( obj ) (GTK_CHECK_CAST( (obj), TYPE_MAINW, Mainw )) #define MAINW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_MAINW, MainwClass )) #define IS_MAINW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_MAINW )) #define IS_MAINW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_MAINW )) /* Get a widget's enclosing Mainw. */ #define GET_MAINW( W ) \ MAINW( idialog_get_root( GTK_WIDGET( W ) ) ) struct _Mainw { iWindow parent_object; /* Our model. */ Workspacegroup *wsg; guint changed_sid; guint destroy_sid; /* Watch for changed on heap and image, and prefs. Use to update * status bar and space free. */ guint imageinfo_changed_sid; guint heap_changed_sid; guint watch_changed_sid; /* Link to progress system. */ guint begin_sid; guint update_sid; guint end_sid; gboolean cancel; /* Batch refresh with this, it's slow. */ guint refresh_timeout; /* Display MB free in tmp, or cells free in heap. */ gboolean free_type; /* View menu show/hide toggle states. The pane states are in the ws as * we need to save them to the ws file. */ gboolean toolbar_visible; gboolean statusbar_visible; /* The kitg the toolkit menu is currently displaying. Use this to * avoid rebuilding the toolkit menu on every tab switch. */ Toolkitgroup *kitg; /* Component widgets. */ Toolkitgroupview *kitgview; GtkWidget *toolbar; GtkWidget *recent_menu; GtkWidget *jump_to_column_menu; GtkWidget *toolkit_menu; Workspacegroupview *wsgview; GtkWidget *statusbar_main; GtkWidget *statusbar; GtkWidget *space_free; GtkWidget *space_free_eb; GtkWidget *progress_box; GtkWidget *progress; }; typedef struct _MainwClass { iWindowClass parent_class; /* My methods. */ } MainwClass; extern GSList *mainw_recent_workspace; extern GSList *mainw_recent_image; extern GSList *mainw_recent_matrix; extern gboolean mainw_auto_recalc; extern gboolean mainw_cancel; void mainw_startup( void ); void mainw_shutdown( void ); void mainw_recent_freeze( void ); void mainw_recent_thaw( void ); void mainw_recent_add( GSList **recent, const char *filename ); Mainw *mainw_pick_one( void ); GType mainw_get_type( void ); void mainw_find_disc( VipsBuf *buf ); void mainw_find_heap( VipsBuf *buf, Heap *heap ); Workspace *mainw_get_workspace( Mainw *mainw ); void mainw_homepage_action_cb( GtkAction *action, iWindow *iwnd ); void mainw_about_action_cb( GtkAction *action, iWindow *iwnd ); void mainw_guide_action_cb( GtkAction *action, iWindow *iwnd ); void mainw_column_new_action_cb( GtkAction *action, Mainw *mainw ); void mainw_workspace_merge( Mainw *mainw ); void mainw_workspace_merge_action_cb( GtkAction *action, Mainw *mainw ); void mainw_layout_action_cb( GtkAction *action, Mainw *mainw ); void mainw_group_action_cb( GtkAction *action, Mainw *mainw ); void mainw_next_error_action_cb( GtkAction *action, Mainw *mainw ); void mainw_open_action_cb( GtkAction *action, Mainw *mainw ); Workspacegroup *mainw_open_workspace( Workspaceroot *wsr, const char *filename ); Mainw *mainw_new( Workspacegroup *wsg ); void mainw_cull( void ); void mainw_layout( void ); ================================================ FILE: src/makehelpindex.pl ================================================ #!/usr/bin/perl # html docs in $VIPSHOME/share/nip2/doc/html include extra anchor tags # generated from \mylabel{} stuff in doc src (nip2-xx/doc/src/nipguide) # # latex source # # \section{Image view window} # \mylabel{sec:view} # # generates html which includes # # # # scan all html files in $VIPSHOME/share/nip2/doc/html for patterns like this, # and generate C along the lines of: # # { "sec:view", "node4.html#nip_label_sec:view" }, # # this is includes in boxes.c ... then on # # box_help( par, "sec:view" ) # # we can pop up a web browser pointing at the right place in the docs $prefix = @ARGV[0]; $docbase = "$prefix/share/doc/nip2/html"; opendir( SDIR, "$docbase" ); while( $filename = readdir SDIR ) { if( $filename =~ /.html$/ ) { open( HTMLFILE, "$docbase/$filename" ); while( ) { if( /"nip_label_([^"]*)"/ ) { print "{ \"$1\", \"$filename#nip_label_" . "$1\" },\n"; } } close( HTMLFILE ); } } closedir( SDIR ); ================================================ FILE: src/managed.c ================================================ /* managed objects ... things like Imageinfo which are lifetime managed by * both the GC and by pointers from C: we need to both mark/sweep and refcount * * abstract class: Managedgvalue, Imageinfo, etc. build off this */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* get -DDEBUG_LEAK from the gcc command-line #define DEBUG */ static iContainerClass *parent_class = NULL; #ifdef DEBUG_LEAK static GSList *managed_all = NULL; #endif /*DEBUG_LEAK*/ #ifdef DEBUG_LEAK static void * managed_print_info( Managed *managed, VipsBuf *buf ) { iobject_info( IOBJECT( managed ), buf ); vips_buf_appends( buf, "\n" ); return( NULL ); } #endif /*DEBUG_LEAK*/ /* Debugging ... check that all manageds have been closed, dump any which * haven't. */ void managed_check_all_destroyed( void ) { #ifdef DEBUG_LEAK if( managed_all ) { char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); printf( "managed_check_all_destroyed:\n" ); slist_map( managed_all, (SListMapFn) managed_print_info, &buf ); printf( "%s", vips_buf_all( &buf ) ); } #endif /*DEBUG_LEAK*/ } void managed_link_heap( Managed *managed, Heap *heap ) { g_assert( !managed->heap ); if( heap == NULL ) heap = reduce_context->heap; managed->heap = heap; g_hash_table_insert( heap->mtable, managed, managed ); managed->attached = TRUE; /* The mtable owns our ref. */ g_object_ref( G_OBJECT( managed ) ); iobject_sink( IOBJECT( managed ) ); } static void managed_unlink_heap( Managed *managed ) { if( managed->attached && managed->heap ) { g_hash_table_remove( managed->heap->mtable, managed ); managed->attached = FALSE; g_object_unref( G_OBJECT( managed ) ); } } /* managed no longer depends upon in. */ void * managed_sub_remove( Managed *in, Managed *managed ) { g_assert( g_slist_find( managed->sub, in ) ); managed->sub = g_slist_remove( managed->sub, in ); managed_destroy_nonheap( in ); return( NULL ); } static void managed_dispose( GObject *gobject ) { Managed *managed = MANAGED( gobject ); #ifdef DEBUG printf( "managed_dispose: " ); iobject_print( IOBJECT( managed ) ); #endif /*DEBUG*/ g_assert( managed->count == 0 ); managed_unlink_heap( managed ); slist_map( managed->sub, (SListMapFn) managed_sub_remove, managed ); g_assert( !managed->sub ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } /* Final death! */ static void managed_finalize( GObject *gobject ) { #ifdef DEBUG Managed *managed = MANAGED( gobject ); printf( "managed_finalize:" ); iobject_print( IOBJECT( managed ) ); #endif /*DEBUG*/ #ifdef DEBUG_LEAK managed_all = g_slist_remove( managed_all, gobject ); #endif /*DEBUG_LEAK*/ G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* _info() is used by itext.c to display managed objects. Don't chain * up, don't print more than one line. */ static void managed_info( iObject *iobject, VipsBuf *buf ) { #ifdef DEBUG Managed *managed = MANAGED( iobject ); vips_buf_appendf( buf, "managed-object %p\n", managed ); vips_buf_appendf( buf, "managed->count = %d\n", managed->count ); vips_buf_appendf( buf, "managed->marked = %d\n", managed->marked ); #endif /*DEBUG*/ vips_buf_appendf( buf, "%s %p", G_OBJECT_TYPE_NAME( iobject ), iobject ); } static void managed_class_init( ManagedClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = managed_dispose; gobject_class->finalize = managed_finalize; iobject_class->info = managed_info; class->keepalive = 0; } static void managed_init( Managed *managed ) { #ifdef DEBUG printf( "managed_init: %p\n", managed ); #endif /*DEBUG*/ managed->heap = NULL; managed->attached = FALSE; /* Init to TRUE, so we won't close until (at least) the next GC. */ managed->marked = TRUE; /* Start with a count of zero (unlike gobject!). We will be deleted * on the next GC unless our caller refs us. */ managed->count = 0; /* When we're unreffed, become a zombie first, then destroy after a * (possibly zero) interval. */ managed->zombie = FALSE; managed->time = 0; managed->sub = NULL; #ifdef DEBUG_LEAK managed_all = g_slist_prepend( managed_all, managed ); #endif /*DEBUG_LEAK*/ } GType managed_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ManagedClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) managed_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Managed ), 32, /* n_preallocs */ (GInstanceInitFunc) managed_init, }; type = g_type_register_static( TYPE_ICONTAINER, "Managed", &info, 0 ); } return( type ); } /* From heap_gc() ... no heap pointers left, delete if there are no * non-heap pointers either. */ void managed_destroy_heap( Managed *managed ) { #ifdef DEBUG printf( "managed_destroy_heap: " ); iobject_print( IOBJECT( managed ) ); #endif /*DEBUG*/ /* All non-heaps gone too? */ if( !managed->count ) IDESTROY( managed ); } /* destroy() for non-heap pointers. */ void * managed_destroy_nonheap( Managed *managed ) { g_assert( managed->count > 0 ); #ifdef DEBUG printf( "managed_destroy_nonheap: count = %d ", managed->count ); iobject_print( IOBJECT( managed ) ); #endif /*DEBUG*/ managed->count--; /* We can't destroy the managed if count == 0 && it's not marked, * since a heap pointer might have been created to it since the last * GC. Queue a GC to clean off stray manageds. */ heap_gc_request( managed->heap ); return( NULL ); } /* Create a new non-heap pointer. */ void managed_dup_nonheap( Managed *managed ) { g_assert( managed->count >= 0 ); managed->count++; #ifdef DEBUG printf( "managed_dup_nonheap: count = %d ", managed->count ); iobject_print( IOBJECT( managed ) ); #endif /*DEBUG*/ } /* managed depends on in ... add a dependency. */ void managed_sub_add( Managed *managed, Managed *in ) { g_assert( managed && in ); managed->sub = g_slist_prepend( managed->sub, in ); managed_dup_nonheap( in ); } /* out needs all of in[], add to sub-mark-list. */ void managed_sub_add_all( Managed *out, int nin, Managed **in ) { int i; if( out ) for( i = 0; i < nin; i++ ) managed_sub_add( out, in[i] ); } static void managed_clear_sub( void *key, Managed *managed ) { managed->marked = FALSE; } void managed_clear( Heap *heap ) { g_hash_table_foreach( heap->mtable, (GHFunc) managed_clear_sub, NULL ); } /* Mark as being used ... also mark all sub-objects. */ void managed_mark( Managed *managed ) { if( !managed->marked ) { managed->marked = TRUE; (void) slist_map( managed->sub, (SListMapFn) managed_mark, NULL ); } } /* Use a timer to remove unreffed keepalive objects after some * interval. */ static GTimer *zombie_timer = NULL; static double zombie_elapsed; static gboolean managed_free_unused_sub( void *key, Managed *managed, gboolean *changed ) { ManagedClass *managed_class = MANAGED_GET_CLASS( managed ); Heap *heap = managed->heap; gboolean remove = FALSE; if( !managed->marked && !managed->count ) { if( !managed->zombie ) { /* Unreffed, but not marked as a zombie. */ #ifdef DEBUG printf( "managed_free: zombiefying: " ); iobject_print( IOBJECT( managed ) ); #endif /*DEBUG*/ managed->zombie = TRUE; managed->time = zombie_elapsed; } } else { if( managed->zombie ) { /* Reffed, but marked as a zombie. Back to life again. */ #ifdef DEBUG printf( "managed_free: resuscitating: " ); iobject_print( IOBJECT( managed ) ); #endif /*DEBUG*/ managed->zombie = FALSE; managed->time = 0; } } /* Is this an old zombie? Or a not-so-old one and we're flushing? * Junk. */ if( managed->zombie && zombie_elapsed - managed->time >= managed_class->keepalive ) remove = TRUE; if( managed->zombie && heap->flush ) remove = TRUE; if( remove ) { #ifdef DEBUG printf( "managed_free: closing unreferenced object: " ); iobject_print( IOBJECT( managed ) ); printf( "managed_free: after %g s as a zombie\n", zombie_elapsed - managed->time ); #endif /*DEBUG*/ /* We will return TRUE to unlink us from the hash table. Stop * managed_dispose unlinking for us, and drop the hash table's * reference. */ managed->attached = FALSE; managed_destroy_heap( managed ); g_object_unref( G_OBJECT( managed ) ); *changed = TRUE; } return( remove ); } /* Make one sweep and destroy all unused managed objects. Return TRUE if we * removed any. */ gboolean managed_free_unused( Heap *heap ) { gboolean changed; if( !zombie_timer ) zombie_timer = g_timer_new(); zombie_elapsed = g_timer_elapsed( zombie_timer, NULL ); changed = FALSE; g_hash_table_foreach_remove( heap->mtable, (GHRFunc) managed_free_unused_sub, &changed ); return( changed ); } ================================================ FILE: src/managed.h ================================================ /* managed objects ... things like Imageinfo which are lifetime managed by * both the GC and by pointers from C: we need to both mark/sweep and refcount * * abstract class: Managedgvalue, Imageinfo, etc. build off this */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MANAGED (managed_get_type()) #define MANAGED( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_MANAGED, Managed )) #define MANAGED_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_MANAGED, ManagedClass)) #define IS_MANAGED( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_MANAGED )) #define IS_MANAGED_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_MANAGED )) #define MANAGED_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_MANAGED, ManagedClass )) #define MANAGED_UNREF( X ) { \ if( X ) { \ managed_destroy_nonheap( MANAGED( X ) ); \ X = NULL; \ } \ } #define MANAGED_REF( X ) managed_dup_nonheap( MANAGED( X ) ) struct _Managed { iContainer parent_object; /* Can't just set ->heap = NULL to mean unattached, our subclasses * rely on ->heap being valid even during dispose. */ Heap *heap; /* Heap we are attached to */ gboolean attached; /* If we are attached to the heap */ gboolean marked; /* For mark-sweep */ int count; /* Number of non-heap pointers to us */ gboolean zombie; /* Unreffed, but being kept alive */ double time; /* When we became a zombie */ /* FIXME ... This should go with vips8: it does dependency tracking for us. */ GSList *sub; /* Sub-objects ... mark these if we mark this */ /* Set by subclasses as part of construction. */ guint hash; }; typedef struct _ManagedClass { iContainerClass parent_class; /* How long after zombiefying before we unref. */ double keepalive; } ManagedClass; void managed_check_all_destroyed( void ); void managed_link_heap( Managed *managed, Heap *heap ); void managed_destroy_heap( Managed *managed ); void *managed_destroy_nonheap( Managed *managed ); void managed_dup_nonheap( Managed *managed ); void *managed_sub_remove( Managed *in, Managed *managed ); void managed_sub_add( Managed *managed, Managed *in ); void managed_sub_add_all( Managed *out, int nin, Managed **in ); GType managed_get_type( void ); void managed_clear( Heap *heap ); void managed_mark( Managed *managed ); gboolean managed_free_unused( Heap *heap ); ================================================ FILE: src/managedfile.c ================================================ /* a managed FILE* ... for lazy file read */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static ManagedClass *parent_class = NULL; static void managedfile_dispose( GObject *gobject ) { Managedfile *managedfile = MANAGEDFILE( gobject ); #ifdef DEBUG printf( "managedfile_dispose: " ); iobject_print( IOBJECT( managedfile ) ); #endif /*DEBUG*/ IM_FREEF( ifile_close, managedfile->file ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void managedfile_info( iObject *iobject, VipsBuf *buf ) { Managedfile *managedfile = MANAGEDFILE( iobject ); vips_buf_appendf( buf, "managedfile->fp = %p\n", managedfile->file->fp ); vips_buf_appendf( buf, "managedfile->file->filename = %s\n", managedfile->file->fname ); vips_buf_appendf( buf, "managedfile->file->last_errno = %d\n", managedfile->file->last_errno ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); } static void managedfile_class_init( ManagedfileClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = managedfile_dispose; iobject_class->info = managedfile_info; } static void managedfile_init( Managedfile *managedfile ) { #ifdef DEBUG printf( "managedfile_init: %p\n", managedfile ); #endif /*DEBUG*/ managedfile->file = NULL; } GType managedfile_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ManagedfileClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) managedfile_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Managedfile ), 32, /* n_preallocs */ (GInstanceInitFunc) managedfile_init, }; type = g_type_register_static( TYPE_MANAGED, "Managedfile", &info, 0 ); } return( type ); } Managedfile * managedfile_new( Heap *heap, const char *filename ) { Managedfile *managedfile; iOpenFile *file; #ifdef DEBUG printf( "managedfile_new: %p: %s\n", managedfile, filename ); #endif /*DEBUG*/ if( !(file = ifile_open_read( "%s", filename )) ) return( NULL ); managedfile = g_object_new( TYPE_MANAGEDFILE, NULL ); managed_link_heap( MANAGED( managedfile ), heap ); managedfile->file = file; MANAGED( managedfile )->hash = g_str_hash( filename ); return( managedfile ); } int managedfile_getc( Managedfile *managedfile ) { int ch = ifile_getc( managedfile->file ); #ifdef DEBUG { char in[2]; char out[3]; in[0] = ch; in[1] = '\0'; my_strecpy( out, in, FALSE ); printf( "managedfile_getc: '%s' (%d)\n", out, ch ); } #endif /*DEBUG*/ return( ch ); } ================================================ FILE: src/managedfile.h ================================================ /* a managed FILE* ... for lazy file read */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MANAGEDFILE (managedfile_get_type()) #define MANAGEDFILE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_MANAGEDFILE, Managedfile )) #define MANAGEDFILE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_MANAGEDFILE, ManagedfileClass)) #define IS_MANAGEDFILE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_MANAGEDFILE )) #define IS_MANAGEDFILE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_MANAGEDFILE )) #define MANAGEDFILE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_MANAGEDFILE, ManagedfileClass )) struct _Managedfile { Managed parent_object; iOpenFile *file; }; typedef struct _ManagedfileClass { ManagedClass parent_class; } ManagedfileClass; GType managedfile_get_type( void ); Managedfile *managedfile_new( Heap *heap, const char *filename ); int managedfile_getc( Managedfile *managedfile ); ================================================ FILE: src/managedgobject.c ================================================ /* a managed gobject */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static ManagedClass *parent_class = NULL; static void managedgobject_dispose( GObject *gobject ) { Managedgobject *managedgobject = MANAGEDGOBJECT( gobject ); #ifdef DEBUG printf( "managedgobject_dispose: " ); iobject_print( IOBJECT( managedgobject ) ); #endif /*DEBUG*/ IM_FREEF( g_object_unref, managedgobject->object ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void managedgobject_info( iObject *iobject, VipsBuf *buf ) { Managedgobject *managedgobject = MANAGEDGOBJECT( iobject ); if( VIPS_IS_OBJECT( managedgobject->object ) ) vips_object_summary( VIPS_OBJECT( managedgobject->object ), buf ); else IOBJECT_CLASS( parent_class )->info( iobject, buf ); } static void managedgobject_class_init( ManagedgobjectClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = managedgobject_dispose; iobject_class->info = managedgobject_info; } static void managedgobject_init( Managedgobject *managedgobject ) { #ifdef DEBUG printf( "managedgobject_init: %p\n", managedgobject ); #endif /*DEBUG*/ managedgobject->object = NULL; } GType managedgobject_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ManagedgobjectClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) managedgobject_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Managedgobject ), 32, /* n_preallocs */ (GInstanceInitFunc) managedgobject_init, }; type = g_type_register_static( TYPE_MANAGED, "Managedgobject", &info, 0 ); } return( type ); } Managedgobject * managedgobject_new( Heap *heap, GObject *object ) { Managedgobject *managedgobject = g_object_new( TYPE_MANAGEDGOBJECT, NULL ); managed_link_heap( MANAGED( managedgobject ), heap ); managedgobject->object = object; g_object_ref( object ); MANAGED( managedgobject )->hash = GPOINTER_TO_UINT( object ); return( managedgobject ); } ================================================ FILE: src/managedgobject.h ================================================ /* a managed gobject */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MANAGEDGOBJECT (managedgobject_get_type()) #define MANAGEDGOBJECT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_MANAGEDGOBJECT, Managedgobject )) #define MANAGEDGOBJECT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_MANAGEDGOBJECT, ManagedgobjectClass)) #define IS_MANAGEDGOBJECT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_MANAGEDGOBJECT )) #define IS_MANAGEDGOBJECT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_MANAGEDGOBJECT )) #define MANAGEDGOBJECT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_MANAGEDGOBJECT, ManagedgobjectClass )) struct _Managedgobject { Managed parent_object; GObject *object; }; typedef struct _ManagedgobjectClass { ManagedClass parent_class; } ManagedgobjectClass; GType managedgobject_get_type( void ); Managedgobject *managedgobject_new( Heap *heap, GObject *value ); ================================================ FILE: src/managedgvalue.c ================================================ /* a managedgvalue gvalue */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static ManagedClass *parent_class = NULL; static void managedgvalue_dispose( GObject *gobject ) { Managedgvalue *managedgvalue = MANAGEDGVALUE( gobject ); #ifdef DEBUG printf( "managedgvalue_dispose: " ); iobject_print( IOBJECT( managedgvalue ) ); #endif /*DEBUG*/ g_value_unset( &managedgvalue->value ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void managedgvalue_info( iObject *iobject, VipsBuf *buf ) { Managedgvalue *managedgvalue = MANAGEDGVALUE( iobject ); char *value_str; value_str = g_strdup_value_contents( &managedgvalue->value ); vips_buf_appendf( buf, "managedgvalue->value = %s\n", value_str ); g_free( value_str ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); } static void managedgvalue_class_init( ManagedgvalueClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = managedgvalue_dispose; iobject_class->info = managedgvalue_info; } static void managedgvalue_init( Managedgvalue *managedgvalue ) { #ifdef DEBUG printf( "managedgvalue_init: %p\n", managedgvalue ); #endif /*DEBUG*/ memset( &managedgvalue->value, 0, sizeof( GValue ) ); } GType managedgvalue_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ManagedgvalueClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) managedgvalue_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Managedgvalue ), 32, /* n_preallocs */ (GInstanceInitFunc) managedgvalue_init, }; type = g_type_register_static( TYPE_MANAGED, "Managedgvalue", &info, 0 ); } return( type ); } void managedgvalue_set_value( Managedgvalue *managedgvalue, GValue *value ) { g_value_unset( &managedgvalue->value ); g_value_init( &managedgvalue->value, G_VALUE_TYPE( value ) ); g_value_copy( &managedgvalue->value, value ); } Managedgvalue * managedgvalue_new( Heap *heap, GValue *value ) { Managedgvalue *managedgvalue = g_object_new( TYPE_MANAGEDGVALUE, NULL ); managed_link_heap( MANAGED( managedgvalue ), heap ); managedgvalue_set_value( managedgvalue, value ); /* Not a very good hash. */ MANAGED( managedgvalue )->hash = (guint) G_VALUE_TYPE( value ); return( managedgvalue ); } ================================================ FILE: src/managedgvalue.h ================================================ /* a managed gvalue */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MANAGEDGVALUE (managedgvalue_get_type()) #define MANAGEDGVALUE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_MANAGEDGVALUE, Managedgvalue )) #define MANAGEDGVALUE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_MANAGEDGVALUE, ManagedgvalueClass)) #define IS_MANAGEDGVALUE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_MANAGEDGVALUE )) #define IS_MANAGEDGVALUE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_MANAGEDGVALUE )) #define MANAGEDGVALUE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_MANAGEDGVALUE, ManagedgvalueClass )) struct _Managedgvalue { Managed parent_object; GValue value; }; typedef struct _ManagedgvalueClass { ManagedClass parent_class; } ManagedgvalueClass; GType managedgvalue_get_type( void ); void managedgvalue_set_value( Managedgvalue *managedgvalue, GValue *value ); Managedgvalue *managedgvalue_new( Heap *heap, GValue *value ); ================================================ FILE: src/managedstring.c ================================================ /* a managed FILE* ... for lazy file read */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static ManagedClass *parent_class = NULL; /* Track all instances here. */ static GHashTable *managedstring_all = NULL; #ifdef DEBUG /* Number of managed strings, number we have expanded to the heap. */ int managed_total = 0; int managed_expanded = 0; #endif /*DEBUG*/ static void managedstring_finalize( GObject *gobject ) { Managedstring *managedstring = MANAGEDSTRING( gobject ); #ifdef DEBUG printf( "managedstring_finalize: \"%s\", ", managedstring->string ); iobject_print( IOBJECT( managedstring ) ); #endif /*DEBUG*/ #ifdef DEBUG { PElement pe; PEPOINTE( &pe, &managedstring->e ); if( !PEISNOVAL( &pe ) ) managed_expanded -= 1; managed_total -= 1; } #endif /*DEBUG*/ heap_unregister_element( MANAGED( managedstring )->heap, &managedstring->e ); g_hash_table_remove( managedstring_all, managedstring ); IM_FREE( managedstring->string ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void managedstring_info( iObject *iobject, VipsBuf *buf ) { Managedstring *managedstring = MANAGEDSTRING( iobject ); vips_buf_appendf( buf, "managedstring->string = \"%s\"\n", managedstring->string ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); } /* Hash and equality for a managed string: we need the string and the heap to * match. */ static unsigned int managedstring_hash( Managedstring *managedstring ) { return( g_str_hash( managedstring->string ) | GPOINTER_TO_UINT( ((Managed *) managedstring)->heap ) ); } static gboolean managedstring_equal( Managedstring *a, Managedstring *b ) { return( ((Managed *) a)->heap == ((Managed *) b)->heap && g_str_equal( a->string, b->string ) ); } static void managedstring_all_init( void ) { if( !managedstring_all ) managedstring_all = g_hash_table_new( (GHashFunc) managedstring_hash, (GEqualFunc) managedstring_equal ); } static void managedstring_class_init( ManagedstringClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = managedstring_finalize; iobject_class->info = managedstring_info; managedstring_all_init(); } static void managedstring_init( Managedstring *managedstring ) { #ifdef DEBUG printf( "managedstring_init: %p\n", managedstring ); #endif /*DEBUG*/ #ifdef DEBUG managed_total += 1; #endif /*DEBUG*/ managedstring->string = NULL; managedstring->e.type = ELEMENT_NOVAL; managedstring->e.ele = NULL; } GType managedstring_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ManagedstringClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) managedstring_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Managedstring ), 32, /* n_preallocs */ (GInstanceInitFunc) managedstring_init, }; type = g_type_register_static( TYPE_MANAGED, "Managedstring", &info, 0 ); } return( type ); } static Managedstring * managedstring_new( Heap *heap, const char *string ) { Managedstring *managedstring; #ifdef DEBUG printf( "managedstring_new: %p, %s\n", heap, string ); #endif /*DEBUG*/ /* Disallow "" as string, we want to represent that as []. */ g_assert( strcmp( string, "" ) != 0 ); managedstring = g_object_new( TYPE_MANAGEDSTRING, NULL ); managed_link_heap( MANAGED( managedstring ), heap ); heap_register_element( heap, &managedstring->e ); if( !(managedstring->string = im_strdup( NULL, string )) ) return( NULL ); g_assert( !g_hash_table_lookup( managedstring_all, managedstring ) ); g_hash_table_insert( managedstring_all, managedstring, managedstring ); MANAGED( managedstring )->hash = managedstring_hash( managedstring ); return( managedstring ); } Managedstring * managedstring_lookup( Heap *heap, const char *string ) { Managedstring managedstring; ((Managed *) &managedstring)->heap = heap; managedstring.string = string; managedstring_all_init(); return( g_hash_table_lookup( managedstring_all, &managedstring ) ); } Managedstring * managedstring_find( Heap *heap, const char *string ) { Managedstring *managedstring; if( !(managedstring = managedstring_lookup( heap, string )) ) if( !(managedstring = managedstring_new( heap, string )) ) return( NULL ); return( managedstring ); } gboolean managedstring_get( Managedstring *managedstring, PElement *out ) { PElement pe; PEPOINTE( &pe, &managedstring->e ); if( PEISNOVAL( &pe ) ) { if( !heap_string_new( MANAGED( managedstring )->heap, managedstring->string, &pe ) ) return( FALSE ); #ifdef DEBUG managed_expanded += 1; printf( "expanding %s to the heap\n", managedstring->string ); printf( "\t(%d of %d now expanded)\n", managed_expanded, managed_total ); #endif /*DEBUG*/ } PEPUTE( out, &managedstring->e ); return( TRUE ); } ================================================ FILE: src/managedstring.h ================================================ /* a managed STRING* ... for lazy string read */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These strings are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MANAGEDSTRING (managedstring_get_type()) #define MANAGEDSTRING( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_MANAGEDSTRING, Managedstring )) #define MANAGEDSTRING_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_MANAGEDSTRING, ManagedstringClass)) #define IS_MANAGEDSTRING( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_MANAGEDSTRING )) #define IS_MANAGEDSTRING_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_MANAGEDSTRING )) #define MANAGEDSTRING_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_MANAGEDSTRING, ManagedstringClass )) struct _Managedstring { Managed parent_object; const char *string; Element e; /* Points to compiled string */ }; typedef struct _ManagedstringClass { ManagedClass parent_class; } ManagedstringClass; GType managedstring_get_type( void ); Managedstring *managedstring_find( Heap *heap, const char *string ); gboolean managedstring_get( Managedstring *managedstring, PElement *out ); ================================================ FILE: src/matrix.c ================================================ /* an input matrix */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void matrix_finalize( GObject *gobject ) { Matrix *matrix; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_MATRIX( gobject ) ); matrix = MATRIX( gobject ); #ifdef DEBUG printf( "matrix_finalize\n" ); #endif /*DEBUG*/ /* My instance finalize stuff. */ IM_FREE( matrix->value.coeff ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Rearrange our model for a new width/height. */ gboolean matrix_value_resize( MatrixValue *value, int width, int height ) { double *coeff; int x, y, i; if( width == value->width && height == value->height ) return( TRUE ); if( !(coeff = IARRAY( NULL, width * height, double )) ) return( FALSE ); /* Set what we can with values from the old matrix. */ for( i = 0, y = 0; y < height; y++ ) for( x = 0; x < width; x++, i++ ) if( y < value->height && x < value->width ) coeff[i] = value->coeff[x + y * value->width]; else coeff[i] = 0.0; /* Install new values. */ IM_FREE( value->coeff ); value->coeff = coeff; value->width = width; value->height = height; return( TRUE ); } /* Widgets for matrix edit. */ typedef struct _MatrixEdit { iDialog *idlg; Matrix *matrix; GtkWidget *width; GtkWidget *height; GtkWidget *display; } MatrixEdit; /* Done button hit. */ /*ARGSUSED*/ static void matrix_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { MatrixEdit *eds = (MatrixEdit *) client; int width, height; /* Parse values. We have to scan before we resize in case we are * sizing smaller and we have unscanned changes at the edges. */ view_scan_all(); eds->matrix->display = (MatrixDisplayType) gtk_combo_box_get_active( GTK_COMBO_BOX( eds->display ) ); if( !get_geditable_pint( eds->width, &width ) || !get_geditable_pint( eds->height, &height ) || !matrix_value_resize( &eds->matrix->value, width, height ) ) { nfn( sys, IWINDOW_ERROR ); return; } /* Rebuild object. */ classmodel_update( CLASSMODEL( eds->matrix ) ); symbol_recalculate_all(); nfn( sys, IWINDOW_YES ); } /* Build the insides of matrix edit. */ static void matrix_buildedit( iDialog *idlg, GtkWidget *work, MatrixEdit *eds ) { Matrix *matrix = eds->matrix; GtkSizeGroup *group; /* Index with MatrixType. */ static const char *display_names[] = { N_( "Text" ), N_( "Sliders" ), N_( "Toggle buttons" ), N_( "Text, plus scale and offset" ) }; group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL ); eds->width = build_glabeltext4( work, group, "Width" ); idialog_init_entry( idlg, eds->width, "Width of matrix", "%d", matrix->value.width ); eds->height = build_glabeltext4( work, group, "Height" ); idialog_init_entry( idlg, eds->height, "Height of matrix", "%d", matrix->value.height ); eds->display = build_goption( work, group, _( "Display as" ), display_names, IM_NUMBER( display_names ), NULL, NULL ); gtk_combo_box_set_active( GTK_COMBO_BOX( eds->display ), matrix->display ); UNREF( group ); gtk_widget_show_all( work ); } static View * matrix_view_new( Model *model, View *parent ) { return( matrixview_new() ); } /* Pop up a matrix edit box. */ static void matrix_edit( GtkWidget *parent, Model *model ) { Matrix *matrix = MATRIX( model ); MatrixEdit *eds = INEW( NULL, MatrixEdit ); GtkWidget *idlg; eds->matrix = matrix; idlg = idialog_new(); iwindow_set_title( IWINDOW( idlg ), _( "Edit %s %s" ), IOBJECT_GET_CLASS_NAME( model ), IOBJECT( HEAPMODEL( model )->row )->name ); idialog_set_build( IDIALOG( idlg ), (iWindowBuildFn) matrix_buildedit, eds, NULL, NULL ); idialog_set_callbacks( IDIALOG( idlg ), iwindow_true_cb, NULL, idialog_free_client, eds ); idialog_add_ok( IDIALOG( idlg ), matrix_done_cb, _( "Set %s" ), IOBJECT_GET_CLASS_NAME( model ) ); iwindow_set_parent( IWINDOW( idlg ), parent ); idialog_set_iobject( IDIALOG( idlg ), IOBJECT( model ) ); iwindow_build( IWINDOW( idlg ) ); gtk_widget_show( GTK_WIDGET( idlg ) ); } static gboolean matrix_graphic_save( Classmodel *classmodel, GtkWidget *parent, const char *filename ) { Matrix *matrix = MATRIX( classmodel ); DOUBLEMASK *dmask; char buf[FILENAME_MAX]; if( !(dmask = matrix_model_to_dmask( matrix )) ) return( FALSE ); /* We don't want $VAR etc. in the filename we pass down to the file * ops. */ im_strncpy( buf, filename, FILENAME_MAX ); path_expand( buf ); if( im_write_dmask_name( dmask, buf ) ) { error_vips_all(); IM_FREEF( im_free_dmask, dmask ); return( FALSE ); } IM_FREEF( im_free_dmask, dmask ); mainw_recent_add( &mainw_recent_matrix, filename ); return( TRUE ); } static gboolean matrix_graphic_replace( Classmodel *classmodel, GtkWidget *parent, const char *filename ) { Matrix *matrix = MATRIX( classmodel ); Row *row = HEAPMODEL( matrix )->row; iText *itext = ITEXT( HEAPMODEL( matrix )->rhs->itext ); DOUBLEMASK *dmask; char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); /* We don't want $VAR etc. in the filename we pass down to the file * ops. */ im_strncpy( txt, filename, FILENAME_MAX ); path_expand( txt ); if( !(dmask = im_read_dmask( txt )) ) { error_vips_all(); return( FALSE ); } matrix_dmask_to_ip( dmask, &buf ); im_free_dmask( dmask ); if( itext_set_formula( itext, vips_buf_all( &buf ) ) ) { itext_set_edited( itext, TRUE ); (void) expr_dirty( row->expr, link_serial_new() ); } mainw_recent_add( &mainw_recent_matrix, filename ); return( TRUE ); } /* Members of matrix we automate. */ static ClassmodelMember matrix_members[] = { { CLASSMODEL_MEMBER_MATRIX, NULL, 0, MEMBER_VALUE, NULL, N_( "Value" ), G_STRUCT_OFFSET( Matrix, value ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_SCALE, "scale", N_( "Scale" ), G_STRUCT_OFFSET( Matrix, scale ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_OFFSET, "offset", N_( "Offset" ), G_STRUCT_OFFSET( Matrix, offset ) }, { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_FILENAME, "filename", N_( "Filename" ), G_STRUCT_OFFSET( Classmodel, filename ) }, { CLASSMODEL_MEMBER_ENUM, NULL, MATRIX_DISPLAY_LAST - 1, MEMBER_DISPLAY, "display", N_( "Display" ), G_STRUCT_OFFSET( Matrix, display ) } }; static void matrix_class_init( MatrixClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = matrix_finalize; iobject_class->user_name = _( "Matrix" ); model_class->view_new = matrix_view_new; model_class->edit = matrix_edit; classmodel_class->graphic_save = matrix_graphic_save; classmodel_class->graphic_replace = matrix_graphic_replace; classmodel_class->filetype = filesel_type_matrix; classmodel_class->filetype_pref = "MATRIX_FILE_TYPE"; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = matrix_members; classmodel_class->n_members = IM_NUMBER( matrix_members ); } static void matrix_init( Matrix *matrix ) { #ifdef DEBUG printf( "matrix_init\n" ); #endif /*DEBUG*/ matrix->value.coeff = NULL; matrix->value.width = 0; matrix->value.height = 0; matrix->display = MATRIX_DISPLAY_TEXT; matrix->scale = 1.0; matrix->offset = 0.0; matrix->selected = FALSE; iobject_set( IOBJECT( matrix ), CLASS_MATRIX, NULL ); } GtkType matrix_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( MatrixClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) matrix_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Matrix ), 32, /* n_preallocs */ (GInstanceInitFunc) matrix_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Matrix", &info, 0 ); } return( type ); } void matrix_select( Matrix *matrix, int left, int top, int width, int height ) { if( !matrix->selected || matrix->range.left != left || matrix->range.top != top || matrix->range.width != width || matrix->range.height != height ) { Row *row = HEAPMODEL( matrix )->row; #ifdef DEBUG printf( "matrix_select: " "left=%d, top = %d, width = %d, height = %d\n", left, top, width, height ); #endif /*DEBUG*/ matrix->selected = TRUE; matrix->range.left = left; matrix->range.top = top; matrix->range.width = width; matrix->range.height = height; iobject_changed( IOBJECT( matrix ) ); /* Also make sure this row is selected. */ row_select_ensure( row ); /* The range of cells selected has changed, so the workspace * must update the status line too. row_select_ensure() only * spots row on/off selects. Yuk! */ iobject_changed( IOBJECT( row->ws ) ); } } void matrix_deselect( Matrix *matrix ) { if( matrix->selected ) { Row *row = HEAPMODEL( matrix )->row; #ifdef DEBUG printf( "matrix_deselect\n" ); #endif /*DEBUG*/ matrix->selected = FALSE; iobject_changed( IOBJECT( matrix ) ); /* Also make sure this row is not selected. */ row_deselect( row ); } } /* Guess a display type from a filename. */ static int matrix_guess_display( const char *fname ) { /* Choose display type based on filename suffix ... rec * displays as 1, mor displays as 2, .con displays as 3, all others * display as 0. Keep in sync with MatrixDisplayType. */ static const FileselFileType *types[] = { &filesel_xfile_type, // matrix &filesel_rfile_type, // recombination &filesel_mfile_type, // morphology &filesel_cfile_type // convolution }; int i; if( !fname ) return( 0 ); for( i = 0; i < IM_NUMBER( types ); i++ ) if( is_file_type( types[i], fname ) ) return( i ); return( 0 ); } /* Make an ip definition out of a DOUBLEMASK. */ void matrix_dmask_to_ip( DOUBLEMASK *dmask, VipsBuf *buf ) { int x, y; /* Build matrix expression. */ vips_buf_appends( buf, CLASS_MATRIX " " ); vips_buf_appends( buf, "[" ); for( y = 0; y < dmask->ysize; y++ ) { vips_buf_appends( buf, "[" ); for( x = 0; x < dmask->xsize; x++ ) { vips_buf_appendf( buf, "%g", dmask->coeff[x + y*dmask->xsize] ); if( x != dmask->xsize - 1 ) vips_buf_appends( buf, "," ); } vips_buf_appends( buf, "]" ); if( y != dmask->ysize - 1 ) vips_buf_appends( buf, "," ); } vips_buf_appends( buf, "]" ); vips_buf_appendf( buf, "(%g) (%g) \"%s\" %d", dmask->scale, dmask->offset, dmask->filename, matrix_guess_display( dmask->filename ) ); } /* Make a heap object out of a DOUBLEMASK. */ gboolean matrix_dmask_to_heap( Heap *heap, DOUBLEMASK *dmask, PElement *out ) { Symbol *sym = compile_lookup( symbol_root->expr->compile, CLASS_MATRIX ); PElement rhs; if( !sym || !sym->expr || !sym->expr->compile || !heap_copy( heap, sym->expr->compile, out ) ) return( FALSE ); if( !heap_appl_add( heap, out, &rhs ) || !heap_matrix_new( heap, dmask->xsize, dmask->ysize, dmask->coeff, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_real_new( heap, dmask->scale, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_real_new( heap, dmask->offset, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_managedstring_new( heap, dmask->filename, &rhs ) || !heap_appl_add( heap, out, &rhs ) || !heap_real_new( heap, matrix_guess_display( dmask->filename ), &rhs ) ) return( FALSE ); return( TRUE ); } /* Cast an IMASK to a DMASK. */ DOUBLEMASK * matrix_imask_to_dmask( INTMASK *imask ) { DOUBLEMASK *dmask; int i; if( !(dmask = im_create_dmask( imask->filename, imask->xsize, imask->ysize )) ) { error_vips_all(); return( NULL ); } dmask->scale = imask->scale; dmask->offset = imask->offset; for( i = 0; i < imask->xsize * imask->ysize; i++ ) dmask->coeff[i] = imask->coeff[i]; return( dmask ); } /* Cast a DMASK to an IMASK. */ INTMASK * matrix_dmask_to_imask( DOUBLEMASK *dmask ) { INTMASK *imask; int i; if( !(imask = im_create_imask( dmask->filename, dmask->xsize, dmask->ysize )) ) { error_vips_all(); 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]; return( imask ); } /* Make a heap object out of an INTMASK. */ gboolean matrix_imask_to_heap( Heap *heap, INTMASK *imask, PElement *out ) { DOUBLEMASK *dmask; if( !(dmask = matrix_imask_to_dmask( imask )) ) return( FALSE ); if( !matrix_dmask_to_heap( heap, dmask, out ) ) { im_free_dmask( dmask ); return( FALSE ); } im_free_dmask( dmask ); return( TRUE ); } /* Make a DOUBLEMASK out of an ip value. */ DOUBLEMASK * matrix_ip_to_dmask( PElement *root ) { char buf[MAX_STRSIZE]; char name[FILENAME_MAX]; DOUBLEMASK *dmask; double scale, offset; char *filename; int width, height; if( !class_get_member_matrix_size( root, MEMBER_VALUE, &width, &height ) ) return( NULL ); if( class_get_member_string( root, MEMBER_FILENAME, buf, MAX_STRSIZE ) ) filename = buf; else { if( !temp_name( name, "mat" ) ) return( NULL ); filename = name; } if( !(dmask = im_create_dmask( filename, width, height )) ) { error_vips_all(); return( NULL ); } if( !class_get_member_matrix( root, MEMBER_VALUE, dmask->coeff, width * height, &width, &height ) ) { IM_FREEF( im_free_dmask, dmask ); return( FALSE ); } if( !class_get_member_real( root, MEMBER_SCALE, &scale ) ) scale = 1.0; if( !class_get_member_real( root, MEMBER_OFFSET, &offset ) ) offset = 0.0; dmask->scale = scale; dmask->offset = offset; return( dmask ); } /* Make an INTMASK out of an ip value. */ INTMASK * matrix_ip_to_imask( PElement *root ) { DOUBLEMASK *dmask; INTMASK *imask; if( !(dmask = matrix_ip_to_dmask( root )) ) return( NULL ); if( !(imask = matrix_dmask_to_imask( dmask )) ) { IM_FREEF( im_free_dmask, dmask ); return( NULL ); } return( imask ); } DOUBLEMASK * matrix_model_to_dmask( Matrix *matrix ) { DOUBLEMASK *dmask; int i; if( !(dmask = im_create_dmask( CLASSMODEL( matrix )->filename, matrix->value.width, matrix->value.height )) ) { error_vips_all(); return( NULL ); } dmask->scale = matrix->scale; dmask->offset = matrix->offset; for( i = 0; i < matrix->value.width * matrix->value.height; i++ ) dmask->coeff[i] = matrix->value.coeff[i]; return( dmask ); } gboolean matrix_dmask_to_model( Matrix *matrix, DOUBLEMASK *dmask ) { int i; if( !matrix_value_resize( &matrix->value, dmask->xsize, dmask->ysize ) ) return( FALSE ); matrix->scale = dmask->scale; matrix->offset = dmask->offset; for( i = 0; i < matrix->value.width * matrix->value.height; i++ ) matrix->value.coeff[i] = dmask->coeff[i]; matrix->display = (MatrixDisplayType) matrix_guess_display( dmask->filename ); IM_SETSTR( CLASSMODEL( matrix )->filename, dmask->filename ); return( TRUE ); } ================================================ FILE: src/matrix.h ================================================ /* a matrix in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MATRIX (matrix_get_type()) #define MATRIX( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_MATRIX, Matrix )) #define MATRIX_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_MATRIX, MatrixClass)) #define IS_MATRIX( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_MATRIX )) #define IS_MATRIX_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_MATRIX )) #define MATRIX_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_MATRIX, MatrixClass )) /* What kind of ui bits have we asked for for this matrix? */ typedef enum { MATRIX_DISPLAY_TEXT = 0, /* Set of text widgets */ MATRIX_DISPLAY_SLIDER, /* Set of sliders */ MATRIX_DISPLAY_TOGGLE, /* Set of 3 value toggles */ MATRIX_DISPLAY_TEXT_SCALE_OFFSET,/* Text, with scale/offset widgets */ MATRIX_DISPLAY_LAST } MatrixDisplayType; typedef struct _Matrix { Classmodel model; /* Base class fields. */ MatrixValue value; /* Other class fields. */ MatrixDisplayType display; /* Display as */ double scale; double offset; /* Is there a current selection on the matrixview? And if there is, * the cells it covers. */ gboolean selected; Rect range; } Matrix; typedef struct _MatrixClass { ClassmodelClass parent_class; /* My methods. */ } MatrixClass; gboolean matrix_value_resize( MatrixValue *value, int width, int height ); GType matrix_get_type( void ); /* Select rectangular areas of matricies. */ void matrix_select( Matrix *matrix, int left, int top, int width, int height ); void matrix_deselect( Matrix *matrix ); void matrix_dmask_to_ip( DOUBLEMASK *dmask, VipsBuf *buf ); gboolean matrix_dmask_to_heap( Heap *heap, DOUBLEMASK *dmask, PElement *out ); DOUBLEMASK *matrix_imask_to_dmask( INTMASK *imask ); INTMASK *matrix_dmask_to_imask( DOUBLEMASK *dmask ); gboolean matrix_imask_to_heap( Heap *heap, INTMASK *imask, PElement *out ); DOUBLEMASK *matrix_ip_to_dmask( PElement *root ); INTMASK *matrix_ip_to_imask( PElement *root ); DOUBLEMASK *matrix_model_to_dmask( Matrix *matrix ); gboolean matrix_dmask_to_model( Matrix *matrix, DOUBLEMASK *dmask ); ================================================ FILE: src/matrixview.c ================================================ /* run the display for an input matrixview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* 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) )) /* The size in cells at which we switch from displaying the whole matrix to * displaying part of it in a scrolled window. */ static const int matrixview_max_width = 7; static const int matrixview_max_height = 10; /* Show a matrix with fixed-width columns. */ static const int matrixview_column_width = 70; /* Limit number of sub-widgets with this ... could be prefs? */ static const int matrixview_max_cells = 100; static GraphicviewClass *parent_class = NULL; static void matrixview_destroy( GtkObject *object ) { Matrixview *matrixview; g_return_if_fail( object != NULL ); g_return_if_fail( IS_MATRIXVIEW( object ) ); #ifdef DEBUG printf( "matrixview_destroy\n" ); #endif /*DEBUG*/ matrixview = MATRIXVIEW( object ); /* My instance destroy stuff. */ IM_FREEF( g_slist_free, matrixview->items ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static gboolean matrixview_scan_text( Matrixview *matrixview, GtkWidget *txt, double *out, gboolean *changed ) { double v; if( !get_geditable_double( txt, &v ) ) return( FALSE ); if( *out != v ) { *out = v; *changed = TRUE; } return( TRUE ); } /* Search and read all text widgets and refill matrix. set_dirty this symbol * if there was a change. Return non-NULL if we found an error. */ static void * matrixview_scan( View *view ) { Matrixview *matrixview = MATRIXVIEW( view ); Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); int width = matrix->value.width; int height = matrix->value.height; Expr *expr = HEAPMODEL( matrix )->row->expr; gboolean changed; int x, y; GSList *p; #ifdef DEBUG printf( "matrixview_scan\n" ); #endif /*DEBUG*/ /* Should be text widgets there ... either text or tslider. */ if( matrixview->display != MATRIX_DISPLAY_TEXT && matrixview->display != MATRIX_DISPLAY_TEXT_SCALE_OFFSET && matrixview->display != MATRIX_DISPLAY_SLIDER ) return( NULL ); expr_error_clear( expr ); changed = FALSE; /* Check for scale and offset, if present. */ if( matrixview->scale && !matrixview_scan_text( matrixview, matrixview->scale, &matrix->scale, &changed ) ) { expr_error_set( expr ); return( view ); } if( matrixview->offset && !matrixview_scan_text( matrixview, matrixview->offset, &matrix->offset, &changed ) ) { expr_error_set( expr ); return( view ); } /* Loop thru' all matrix widgets. tsliders have text fields we must * scan too. */ if( matrixview->items ) for( p = matrixview->items, y = 0; y < height; y++ ) for( x = 0; x < width; x++, p = p->next ) { GtkWidget *item = GTK_WIDGET( p->data ); GtkWidget *entry = TSLIDER( item )->entry; int i = x + y * width; if( !matrixview_scan_text( matrixview, entry, &matrix->value.coeff[i], &changed ) ) { error_top( _( "Bad value." ) ); error_sub( _( "Cell (%d, %d):\n%s" ), x, y, error_get_sub() ); expr_error_set( expr ); return( view ); } } if( matrixview->store ) { GtkTreeModel *tree = GTK_TREE_MODEL( matrixview->store ); GtkTreeIter iter; gtk_tree_model_get_iter_first( tree, &iter ); for( y = 0; y < height; y++ ) { for( x = 0; x < width; x++ ) { double *out = &matrix->value.coeff[x + y * width]; double d; gtk_tree_model_get( tree, &iter, x, &d, -1 ); if( *out != d ) { *out = d; changed = TRUE; } } gtk_tree_model_iter_next( tree, &iter ); } } if( changed ) classmodel_update( CLASSMODEL( matrix ) ) ; return( VIEW_CLASS( parent_class )->scan( view ) ); } /* Change to a toggle widget. */ /*ARGSUSED*/ static void matrixview_toggle_change_cb( GtkWidget *widget, Matrixview *matrixview ) { Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); int pos = g_slist_index( matrixview->items, widget ); int x = pos % matrixview->width; int y = pos / matrixview->width; int i = x + y * matrix->value.width; #ifdef DEBUG printf( "matrixview_toggle_change_cb\n" ); #endif /*DEBUG*/ /* Cycle value. */ switch( (int) matrix->value.coeff[i] ) { case 0: matrix->value.coeff[i] = 128.0; break; case 255: matrix->value.coeff[i] = 0.0; break; default: matrix->value.coeff[i] = 255.0; break; } classmodel_update( CLASSMODEL( matrix ) ); symbol_recalculate_all(); } /* Build a set of toggle items for a matrix. */ static void matrixview_toggle_build( Matrixview *matrixview ) { Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); int x, y; int cx, cy; matrixview->table = gtk_table_new( matrixview->height, matrixview->width, TRUE ); gtk_box_pack_start( GTK_BOX( matrixview->box ), matrixview->table, FALSE, FALSE, 0 ); /* Find the centre position, if there is one. We give this a special * name; it is highlit by our resource file. */ cx = -1; cy = -1; if( matrix->value.height & 0x1 ) cy = matrix->value.height >> 1; if( matrix->value.width & 0x1 ) cx = matrix->value.width >> 1; /* Build contents. */ for( y = 0; y < matrixview->height; y++ ) for( x = 0; x < matrixview->width; x++ ) { GtkWidget *but; but = gtk_button_new_with_label( "0" ); gtk_signal_connect( GTK_OBJECT( but ), "clicked", GTK_SIGNAL_FUNC( matrixview_toggle_change_cb ), matrixview ); if( x == cx && y == cy ) gtk_widget_set_name( but, "centre_widget" ); /* FIXME ... this b0rks thanks to pangolayout confusion set_fixed( GTK_BIN( but )->child, 1 ); */ gtk_table_attach( GTK_TABLE( matrixview->table ), but, x, x + 1, y, y + 1, GTK_FILL, GTK_FILL, 2, 2 ); matrixview->items = g_slist_append( matrixview->items, but ); } } /* Change to a scale in a Tslider. */ /*ARGSUSED*/ static void matrixview_slider_change_cb( Tslider *tslider, Matrixview *matrixview ) { Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); int pos = g_slist_index( matrixview->items, tslider ); int x = pos % matrixview->width; int y = pos / matrixview->width; int i = x + y * matrix->value.width; g_assert( pos >= 0 ); /* Install value. */ if( matrix->value.coeff[i] != tslider->svalue ) { matrix->value.coeff[i] = tslider->svalue; classmodel_update( CLASSMODEL( matrix ) ); symbol_recalculate_all(); } } /* Build a set of slider items for a matrix. */ static void matrixview_slider_build( Matrixview *matrixview ) { int x, y; matrixview->table = gtk_table_new( matrixview->height, matrixview->width, TRUE ); gtk_box_pack_start( GTK_BOX( matrixview->box ), matrixview->table, TRUE, TRUE, 0 ); for( y = 0; y < matrixview->height; y++ ) for( x = 0; x < matrixview->width; x++ ) { Tslider *tslider = tslider_new(); tslider_set_conversions( tslider, NULL, NULL ); tslider->from = -2; tslider->to = 2; tslider->digits = 3; gtk_signal_connect_object( GTK_OBJECT( tslider ), "text_changed", GTK_SIGNAL_FUNC( view_changed_cb ), GTK_OBJECT( matrixview ) ); gtk_signal_connect_object( GTK_OBJECT( tslider ), "activate", GTK_SIGNAL_FUNC( view_activate_cb ), GTK_OBJECT( matrixview ) ); gtk_signal_connect( GTK_OBJECT( tslider ), "slider_changed", GTK_SIGNAL_FUNC( matrixview_slider_change_cb ), matrixview ); gtk_container_set_border_width( GTK_CONTAINER( tslider ), 2 ); gtk_table_attach_defaults( GTK_TABLE( matrixview->table ), GTK_WIDGET( tslider ), x, x + 1, y, y + 1 ); matrixview->items = g_slist_append( matrixview->items, tslider ); } } static gboolean matrixview_text_focus_in( GtkWidget *entry, GdkEvent *event, void *data ) { gtk_editable_select_region( GTK_EDITABLE( entry ), 0, -1 ); return( FALSE ); } static gboolean matrixview_text_focus_out( GtkWidget *entry, GdkEvent *event, void *data ) { gtk_editable_select_region( GTK_EDITABLE( entry ), 0, 0 ); return( FALSE ); } static void matrixview_text_connect( Matrixview *matrixview, GtkWidget *txt ) { gtk_signal_connect_object( GTK_OBJECT( txt ), "changed", GTK_SIGNAL_FUNC( view_changed_cb ), GTK_OBJECT( matrixview ) ); gtk_signal_connect_object( GTK_OBJECT( txt ), "activate", GTK_SIGNAL_FUNC( view_activate_cb ), GTK_OBJECT( matrixview ) ); /* Select text on focus-in, deselect on focus out. */ gtk_signal_connect( GTK_OBJECT( txt ), "focus_in_event", GTK_SIGNAL_FUNC( matrixview_text_focus_in ), NULL ); gtk_signal_connect( GTK_OBJECT( txt ), "focus_out_event", GTK_SIGNAL_FUNC( matrixview_text_focus_out ), NULL ); } static void matrixview_text_build_scale_offset( Matrixview *matrixview ) { GtkSizeGroup *group; matrixview->cbox = gtk_vbox_new( FALSE, 2 ); gtk_box_pack_end( GTK_BOX( matrixview->box ), GTK_WIDGET( matrixview->cbox ), FALSE, FALSE, 0 ); group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL ); matrixview->scale = build_glabeltext4( matrixview->cbox, group, _( "Scale" ) ); gtk_entry_set_width_chars( GTK_ENTRY( matrixview->scale ), 6 ); matrixview_text_connect( matrixview, matrixview->scale ); matrixview->offset = build_glabeltext4( matrixview->cbox, group, _( "Offset" ) ); gtk_entry_set_width_chars( GTK_ENTRY( matrixview->offset ), 6 ); matrixview_text_connect( matrixview, matrixview->offset ); UNREF( group ); } /* Make a GtkListStore from a MatrixValue. */ GtkListStore * matrixview_liststore_new( MatrixValue *matrixvalue ) { int width = matrixvalue->width; int height = matrixvalue->height; GType *types; int i, y; GtkListStore *store; types = g_new( GType, width ); for( i = 0; i < width; i++ ) types[i] = G_TYPE_DOUBLE; store = gtk_list_store_newv( width, types ); g_free( types ); for( y = 0; y < height; y++ ) { GtkTreeIter iter; gtk_list_store_append( store, &iter ); for( i = 0; i < width; i++ ) gtk_list_store_set( store, &iter, i, matrixvalue->coeff[y * width + i], -1 ); } return( store ); } static void matrixview_edited_cb( GtkCellRendererText *renderer, char *path, char *new_text, void *user_data ) { Matrixview *matrixview = MATRIXVIEW( user_data ); GtkTreeModel *tree = GTK_TREE_MODEL( matrixview->store ); GtkTreeIter iter; if( gtk_tree_model_get_iter_from_string( tree, &iter, path ) ) { int col = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( renderer ), "nip2_column_num" ) ); gtk_list_store_set( GTK_LIST_STORE( tree ), &iter, col, atof( new_text ), -1 ); view_scannable_register( VIEW( matrixview ) ); symbol_recalculate_all(); } } static void matrixview_cell_data_cb( GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree, GtkTreeIter *iter, void *data ) { int col = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( cell ), "nip2_column_num" ) ); double d; char buf[256]; gtk_tree_model_get( tree, iter, col, &d, -1 ); vips_snprintf( buf, 256, "%g", d ); g_object_set( cell, "text", buf, NULL ); } /* Build a set of text items for a matrix. */ static void matrixview_text_build( Matrixview *matrixview ) { Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); int i; GtkTreeViewColumn *column; int cell_height; GtkTreeSelection *selection; if( !matrix->value.coeff ) return; matrixview->store = matrixview_liststore_new( &matrix->value ); matrixview->sheet = gtk_tree_view_new_with_model( GTK_TREE_MODEL( matrixview->store ) ); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( matrixview->sheet ), FALSE ); /* Stops a harmless compiler warning. */ column = NULL; for( i = 0; i < matrix->value.width; i++ ) { GtkCellRenderer *renderer; char buf[256]; renderer = gtk_cell_renderer_text_new(); g_object_set( renderer, "editable", TRUE, NULL ); g_object_set_data( G_OBJECT( renderer ), "nip2_column_num", GINT_TO_POINTER( i ) ); g_signal_connect( G_OBJECT( renderer ), "edited", G_CALLBACK( matrixview_edited_cb ), matrixview ); column = gtk_tree_view_column_new(); gtk_tree_view_column_set_sizing( column, GTK_TREE_VIEW_COLUMN_FIXED ); gtk_tree_view_column_set_fixed_width( column, matrixview_column_width ); im_snprintf( buf, 256, "%d", i ); gtk_tree_view_column_set_title( column, buf ); gtk_tree_view_column_pack_start( column, renderer, FALSE ); gtk_tree_view_column_set_attributes( column, renderer, "text", i, NULL ); gtk_tree_view_column_set_cell_data_func( column, renderer, matrixview_cell_data_cb, NULL, NULL ); gtk_tree_view_append_column( GTK_TREE_VIEW( matrixview->sheet ), column ); } gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW( matrixview->sheet ), TRUE ); gtk_tree_view_column_cell_get_size( column, NULL, NULL, NULL, NULL, &cell_height ); selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( matrixview->sheet ) ); gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE ); gtk_tree_view_set_rubber_banding( GTK_TREE_VIEW( matrixview->sheet ), TRUE ); gtk_tree_view_set_grid_lines( GTK_TREE_VIEW( matrixview->sheet ), GTK_TREE_VIEW_GRID_LINES_BOTH ); if( matrix->value.width > matrixview_max_width || matrix->value.height > matrixview_max_height ) { GtkRequisition requisition; gint spacing; int border; int width, height; if( matrix->value.width > matrixview_max_width ) gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( matrixview->sheet ), TRUE ); matrixview->swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( matrixview->swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_container_add( GTK_CONTAINER( matrixview->swin ), matrixview->sheet ); /* Calculate how big we should make the scrolled window. We * need to leave space for the scrollbars. */ gtk_widget_size_request( gtk_scrolled_window_get_hscrollbar( GTK_SCROLLED_WINDOW( matrixview->swin ) ), &requisition ); gtk_widget_style_get( GTK_WIDGET( matrixview->swin ), "scrollbar-spacing", &spacing, NULL ); border = requisition.height + spacing; /* Subarea of matrix we show, in cells. */ width = IM_MIN( matrix->value.width, matrixview_max_width ); height = IM_MIN( matrix->value.height, matrixview_max_height ); /* Convert to pixels. */ width *= matrixview_column_width; height *= cell_height; /* Will we be showing scrollbars? Need to add a bit. */ if( matrixview->width > matrixview_max_width ) height += border; if( matrixview->height > matrixview_max_height ) width += border; gtk_widget_set_size_request( GTK_WIDGET( matrixview->swin ), width + 5, height + 5 ); gtk_box_pack_start( GTK_BOX( matrixview->box ), matrixview->swin, FALSE, FALSE, 0 ); } else { gtk_box_pack_start( GTK_BOX( matrixview->box ), matrixview->sheet, FALSE, FALSE, 0 ); } if( matrixview->display == MATRIX_DISPLAY_TEXT_SCALE_OFFSET ) /* Make the scale/offset widgets too. */ matrixview_text_build_scale_offset( matrixview ); } /* Set the label on a toggle button to reflect its value. */ static void matrixview_toggle_set_label( GtkWidget *button, double v ) { GtkWidget *label = GTK_BIN( button )->child; g_return_if_fail( GTK_IS_LABEL( label ) ); switch( (int) v ) { case 0: set_glabel( label, "0" ); break; case 255: set_glabel( label, "1" ); break; default: set_glabel( label, "*" ); break; } } /* Refresh a set of toggle items for a matrix. */ static void matrixview_toggle_refresh( Matrixview *matrixview ) { Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); int x, y; GSList *p; for( p = matrixview->items, y = 0; y < matrixview->height; y++ ) for( x = 0; x < matrixview->width; x++, p = p->next ) { GtkWidget *wid = GTK_WIDGET( p->data ); int i = x + y * matrix->value.width; matrixview_toggle_set_label( wid, matrix->value.coeff[i] ); } } /* Refresh a set of slider items for a matrix. */ static void matrixview_slider_refresh( Matrixview *matrixview ) { Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); int x, y; GSList *p; for( p = matrixview->items, y = 0; y < matrixview->height; y++ ) for( x = 0; x < matrixview->width; x++, p = p->next ) { Tslider *tslider = TSLIDER( p->data ); int i = x + y * matrix->value.width; tslider->value = matrix->value.coeff[i]; tslider->svalue = matrix->value.coeff[i]; tslider_changed( tslider ); } } static void matrixview_text_set( Matrixview *matrixview, GtkWidget *txt, double val ) { if( txt ) { gtk_signal_handler_block_by_data( GTK_OBJECT( txt ), matrixview ); set_gentry( txt, "%g", val ); gtk_signal_handler_unblock_by_data( GTK_OBJECT( txt ), matrixview ); } } /* Fill the widgets! */ static void matrixview_text_refresh( Matrixview *matrixview ) { Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); MatrixValue *matrixvalue = &matrix->value; int width = matrixvalue->width; int height = matrixvalue->height; GtkTreeModel *tree = GTK_TREE_MODEL( matrixview->store ); int x, y; GtkTreeIter iter; if( !matrixvalue->coeff ) return; matrixview_text_set( matrixview, matrixview->scale, matrix->scale ); matrixview_text_set( matrixview, matrixview->offset, matrix->offset ); gtk_tree_model_get_iter_first( tree, &iter ); for( y = 0; y < height; y++ ) { for( x = 0; x < width; x++ ) gtk_list_store_set( matrixview->store, &iter, x, matrixvalue->coeff[x + y * width], -1 ); gtk_tree_model_iter_next( tree, &iter ); } } static void matrixview_refresh( vObject *vobject ) { Matrixview *matrixview = MATRIXVIEW( vobject ); Matrix *matrix = MATRIX( VOBJECT( matrixview )->iobject ); gboolean built; gboolean hclip; gboolean vclip; int width, height; int i; built = FALSE; hclip = FALSE; vclip = FALSE; /* Find required size ... limit displays which are tables of widgets * to avoid huge slowness. */ width = matrix->value.width; height = matrix->value.height; if( matrix->display == MATRIX_DISPLAY_TOGGLE || matrix->display == MATRIX_DISPLAY_SLIDER ) { if( width * height > matrixview_max_cells ) { if( width > height ) { width = IM_CLIP( 1, matrixview_max_cells / height, matrix->value.width ); hclip = TRUE; } else { height = IM_CLIP( 1, matrixview_max_cells / width, matrix->value.height ); vclip = TRUE; } } /* Clip twice to make sure we clip in both directions if * necessary. */ if( width * height > matrixview_max_cells ) { if( width > height ) { width = IM_CLIP( 1, matrixview_max_cells / height, matrix->value.width ); hclip = TRUE; } else { height = IM_CLIP( 1, matrixview_max_cells / width, matrix->value.height ); vclip = TRUE; } } } #ifdef DEBUG printf( "matrixview_refresh\n" ); #endif /*DEBUG*/ /* Is there a UI already there we can reuse? Has to be same size and * type. */ if( matrixview->display != matrix->display || matrixview->width != width || matrixview->height != height ) { /* Kill old UI stuff. */ IM_FREEF( gtk_widget_destroy, matrixview->sheet ); IM_FREEF( gtk_widget_destroy, matrixview->table ); IM_FREEF( gtk_widget_destroy, matrixview->swin ); IM_FREEF( g_slist_free, matrixview->items ); IM_FREEF( gtk_widget_destroy, matrixview->cbox ); matrixview->scale = NULL; matrixview->offset = NULL; /* So the builders know how many widgets to make. */ matrixview->width = width; matrixview->height = height; matrixview->display = matrix->display; /* Make new contents. */ switch( matrix->display ) { case MATRIX_DISPLAY_TOGGLE: matrixview_toggle_build( matrixview ); break; case MATRIX_DISPLAY_SLIDER: matrixview_slider_build( matrixview ); break; case MATRIX_DISPLAY_TEXT: case MATRIX_DISPLAY_TEXT_SCALE_OFFSET: matrixview_text_build( matrixview ); break; default: g_assert( FALSE ); } if( hclip ) { gtk_table_resize( GTK_TABLE( matrixview->table ), matrixview->height, matrixview->width + 1 ); for( i = 0; i < matrixview->height; i++ ) { GtkWidget *lab; lab = gtk_label_new( "---" ); gtk_table_attach( GTK_TABLE( matrixview->table ), lab, matrixview->width, matrixview->width + 1, i, i + 1, GTK_FILL, GTK_FILL, 2, 2 ); } } if( vclip ) { gtk_table_resize( GTK_TABLE( matrixview->table ), matrixview->height + 1, matrixview->width ); for( i = 0; i < matrixview->width; i++ ) { GtkWidget *lab; lab = gtk_label_new( "|" ); gtk_table_attach( GTK_TABLE( matrixview->table ), lab, i, i + 1, matrixview->height, matrixview->height + 1, GTK_FILL, GTK_FILL, 2, 2 ); } } built = TRUE; } switch( matrixview->display ) { case MATRIX_DISPLAY_TOGGLE: matrixview_toggle_refresh( matrixview ); break; case MATRIX_DISPLAY_SLIDER: matrixview_slider_refresh( matrixview ); break; case MATRIX_DISPLAY_TEXT: case MATRIX_DISPLAY_TEXT_SCALE_OFFSET: matrixview_text_refresh( matrixview ); break; default: g_assert( FALSE ); } /* If we've built a new display, need to show after _refresh. */ if( built ) { gtk_widget_show_all( GTK_WIDGET( matrixview ) ); view_resize( VIEW( matrixview ) ); } VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void matrixview_class_init( MatrixviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = matrixview_destroy; /* Create signals. */ /* Init methods. */ vobject_class->refresh = matrixview_refresh; view_class->scan = matrixview_scan; } static void matrixview_init( Matrixview *matrixview ) { #ifdef DEBUG printf( "matrixview_init\n" ); #endif /*DEBUG*/ matrixview->box = gtk_hbox_new( FALSE, 12 ); gtk_box_pack_start( GTK_BOX( matrixview ), GTK_WIDGET( matrixview->box ), FALSE, FALSE, 0 ); /* Build on 1st refresh. */ matrixview->store = NULL; matrixview->sheet = NULL; matrixview->swin = NULL; matrixview->table = NULL; matrixview->items = NULL; matrixview->width = -1; matrixview->height = -1; matrixview->cbox = NULL; matrixview->scale = NULL; matrixview->offset = NULL; } GtkType matrixview_get_type( void ) { static GtkType matrixview_type = 0; if( !matrixview_type ) { static const GtkTypeInfo info = { "Matrixview", sizeof( Matrixview ), sizeof( MatrixviewClass ), (GtkClassInitFunc) matrixview_class_init, (GtkObjectInitFunc) matrixview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; matrixview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( matrixview_type ); } View * matrixview_new( void ) { Matrixview *matrixview = gtk_type_new( TYPE_MATRIXVIEW ); return( VIEW( matrixview ) ); } ================================================ FILE: src/matrixview.h ================================================ /* a matrixview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_MATRIXVIEW (matrixview_get_type()) #define MATRIXVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_MATRIXVIEW, Matrixview )) #define MATRIXVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_MATRIXVIEW, MatrixviewClass )) #define IS_MATRIXVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_MATRIXVIEW )) #define IS_MATRIXVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_MATRIXVIEW )) typedef struct _Matrixview { Graphicview parent_object; GtkWidget *box; /* Top level hbox we lay out in */ /* If we're displaying a matrix with a gtktreeview. */ GtkListStore *store; GtkWidget *sheet; GtkWidget *swin; /* Displaying a table of widgets: sliders or toggles. */ GtkWidget *table; /* Matrix table */ GSList *items; /* Widgets for elems */ MatrixDisplayType display; /* What's in items at the mo */ int width; /* Size of mat panel we have */ int height; GtkWidget *cbox; /* Convolution only: scale & offset */ GtkWidget *scale; GtkWidget *offset; } Matrixview; typedef struct _MatrixviewClass { GraphicviewClass parent_class; /* My methods. */ } MatrixviewClass; GtkType matrixview_get_type( void ); View *matrixview_new( void ); ================================================ FILE: src/model.c ================================================ /* abstract base class for things which form the model half of a model/view * pair */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Stuff from bison ... needed as we call the lexer directly to rewrite * expressions. */ #include "parse.h" /* Our signals. */ enum { SIG_SCROLLTO, /* Views should try to make themselves visible */ SIG_LAYOUT, /* Views should lay out their children */ SIG_RESET, /* Reset edit mode in views */ SIG_FRONT, /* Bring views to front */ SIG_DISPLAY, /* Display on/off */ SIG_LAST }; static iContainerClass *parent_class = NULL; static guint model_signals[SIG_LAST] = { 0 }; /* Base model ... built at startup. */ static Model *model_base = NULL; /* All the model classes which can be built from XML. */ static GSList *model_registered_loadable = NULL; /* The loadstate the lexer gets its rename stuff from. */ ModelLoadState *model_loadstate = NULL; /* Rename list functions. */ static void * model_rename_destroy( ModelRename *rename ) { IM_FREE( rename->old_name ); IM_FREE( rename->new_name ); IM_FREE( rename ); return( NULL ); } static ModelRename * model_rename_new( const char *old_name, const char *new_name ) { ModelRename *rename; if( !(rename = INEW( NULL, ModelRename )) ) return( NULL ); rename->old_name = im_strdup( NULL, old_name ); rename->new_name = im_strdup( NULL, new_name ); if( !rename->old_name || !rename->new_name ) { model_rename_destroy( rename ); return( NULL ); } return( rename ); } gboolean model_loadstate_rename_new( ModelLoadState *state, const char *old_name, const char *new_name ) { /* Make a rename, even if old_name == new_name, since we want to have * new_name on the taken list. */ ModelRename *rename; if( !(rename = model_rename_new( old_name, new_name )) ) return( FALSE ); state->renames = g_slist_prepend( state->renames, rename ); return( TRUE ); } static void * model_loadstate_taken_sub( ModelRename *rename, const char *name ) { if( strcmp( rename->new_name, name ) == 0 ) return( rename ); return( NULL ); } /* Is something already being renamed to @name. */ gboolean model_loadstate_taken( ModelLoadState *state, const char *name ) { return( slist_map( state->renames, (SListMapFn) model_loadstate_taken_sub, (char *) name ) != NULL ); } gboolean model_loadstate_column_rename_new( ModelLoadState *state, const char *old_name, const char *new_name ) { if( strcmp( old_name, new_name ) != 0 ) { ModelRename *rename; if( !(rename = model_rename_new( old_name, new_name )) ) return( FALSE ); state->column_renames = g_slist_prepend( state->column_renames, rename ); } return( TRUE ); } /* Is something already being renamed to @name. */ gboolean model_loadstate_column_taken( ModelLoadState *state, const char *name ) { return( !!slist_map( state->column_renames, (SListMapFn) model_loadstate_taken_sub, (char *) name ) ); } void model_loadstate_destroy( ModelLoadState *state ) { /* We are probably registered as the xml error handler ... unregister! */ xmlSetGenericErrorFunc( NULL, NULL ); IM_FREE( state->filename ); IM_FREE( state->filename_user ); IM_FREEF( xmlFreeDoc, state->xdoc ); slist_map( state->renames, (SListMapFn) model_rename_destroy, NULL ); slist_map( state->column_renames, (SListMapFn) model_rename_destroy, NULL ); g_slist_free( state->renames ); if( state->old_dir ) { path_rewrite_add( state->old_dir, NULL, FALSE ); IM_FREE( state->old_dir ); } IM_FREE( state ); } static void model_loadstate_error( ModelLoadState *state, const char *fmt, ... ) { va_list ap; va_start( ap, fmt ); (void) vips_buf_vappendf( &state->error_log, fmt, ap ); va_end( ap ); } static void model_loadstate_error_get( ModelLoadState *state ) { char *utf8; utf8 = f2utf8( vips_buf_all( &state->error_log ) ); error_top( _( "Load failed." ) ); error_sub( _( "Unable to load from file \"%s\". Error log is:\n%s" ), state->filename, utf8 ); g_free( utf8 ); } ModelLoadState * model_loadstate_new( const char *filename, const char *filename_user ) { ModelLoadState *state; if( !(state = INEW( NULL, ModelLoadState )) ) return( NULL ); state->xdoc = NULL; state->renames = NULL; state->column_renames = NULL; state->major = MAJOR_VERSION; state->minor = MINOR_VERSION; state->micro = MICRO_VERSION; state->rewrite_path = FALSE; state->old_dir = FALSE; state->filename = im_strdup( NULL, filename ); if( filename_user ) state->filename_user = im_strdup( NULL, filename_user ); else state->filename_user = im_strdup( NULL, filename ); if( !state->filename || !state->filename_user ) { model_loadstate_destroy( state ); return( NULL ); } vips_buf_init_static( &state->error_log, state->error_log_buffer, MAX_STRSIZE ); xmlSetGenericErrorFunc( state, (xmlGenericErrorFunc) model_loadstate_error ); if( !(state->xdoc = (xmlDoc *) callv_string_filename( (callv_string_fn) xmlParseFile, state->filename, NULL, NULL, NULL )) ) { model_loadstate_error_get( state ); model_loadstate_destroy( state ); return( NULL ); } return( state ); } ModelLoadState * model_loadstate_new_openfile( iOpenFile *of ) { ModelLoadState *state; char load_buffer[MAX_STRSIZE]; if( !(state = INEW( NULL, ModelLoadState )) ) return( NULL ); state->renames = NULL; state->xdoc = NULL; if( !(state->filename = im_strdup( NULL, of->fname )) ) { model_loadstate_destroy( state ); return( NULL ); } vips_buf_init_static( &state->error_log, state->error_log_buffer, MAX_STRSIZE ); xmlSetGenericErrorFunc( state, (xmlGenericErrorFunc) model_loadstate_error ); if( !ifile_read_buffer( of, load_buffer, MAX_STRSIZE ) ) { model_loadstate_destroy( state ); return( NULL ); } if( !(state->xdoc = xmlParseMemory( load_buffer, MAX_STRSIZE )) ) { model_loadstate_error_get( state ); model_loadstate_destroy( state ); return( NULL ); } return( state ); } /* If old_name is on the global rewrite list, rewrite it! Called from the * lexer. */ char * model_loadstate_rewrite_name( char *name ) { ModelLoadState *state = model_loadstate; GSList *i; if( !state || !state->renames ) return( NULL ); for( i = state->renames; i; i = i->next ) { ModelRename *rename = (ModelRename *) (i->data); if( strcmp( name, rename->old_name ) == 0 ) return( rename->new_name ); } return( NULL ); } /* Use the lexer to rewrite an expression, swapping all symbols on the rewrite * list. */ void model_loadstate_rewrite( ModelLoadState *state, char *old_rhs, char *new_rhs ) { int yychar; extern int yylex( void ); model_loadstate = state; attach_input_string( old_rhs ); if( setjmp( parse_error_point ) ) { /* Here for yyerror in lex. Just ignore errors --- the parser * will spot them later anyway. */ model_loadstate = NULL; return; } /* Lex and rewrite. */ state->rewrite_path = FALSE; while( (yychar = yylex()) > 0 ) { /* If we see an Image_file or Matrix_file token, rewrite the * following token if it's a string constant. */ state->rewrite_path = FALSE; if( yychar == TK_IDENT && strcmp( yylval.yy_name, "Image_file" ) == 0 ) state->rewrite_path = TRUE; if( yychar == TK_IDENT && strcmp( yylval.yy_name, "Matrix_file" ) == 0 ) state->rewrite_path = TRUE; free_lex( yychar ); } model_loadstate = NULL; /* Take copy of lexed and rewritten stuff. */ im_strncpy( new_rhs, vips_buf_all( &lex_text ), MAX_STRSIZE ); } View * model_view_new( Model *model, View *parent ) { ModelClass *model_class = MODEL_GET_CLASS( model ); View *view; if( !model_class->view_new ) return( NULL ); view = model_class->view_new( model, parent ); view_link( view, model, parent ); return( view ); } /* Register a model subclass as loadable ... what we allow when we load an * XML node's children. */ void model_register_loadable( ModelClass *model_class ) { model_registered_loadable = g_slist_prepend( model_registered_loadable, model_class ); } void model_scrollto( Model *model, ModelScrollPosition position ) { g_assert( IS_MODEL( model ) ); g_signal_emit( G_OBJECT( model ), model_signals[SIG_SCROLLTO], 0, position ); } void model_layout( Model *model ) { g_assert( IS_MODEL( model ) ); g_signal_emit( G_OBJECT( model ), model_signals[SIG_LAYOUT], 0 ); } void model_front( Model *model ) { g_assert( IS_MODEL( model ) ); g_signal_emit( G_OBJECT( model ), model_signals[SIG_FRONT], 0 ); } void model_display( Model *model, gboolean display ) { if( model ) { g_assert( IS_MODEL( model ) ); g_signal_emit( G_OBJECT( model ), model_signals[SIG_DISPLAY], 0, display ); } } void * model_reset( Model *model ) { g_assert( IS_MODEL( model ) ); g_signal_emit( G_OBJECT( model ), model_signals[SIG_RESET], 0 ); return( NULL ); } void * model_edit( GtkWidget *parent, Model *model ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_class->edit ) model_class->edit( parent, model ); else { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "edit", G_OBJECT_CLASS_NAME( model_class ) ); } return( NULL ); } void * model_header( GtkWidget *parent, Model *model ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_class->header ) model_class->header( parent, model ); else { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "header", G_OBJECT_CLASS_NAME( model_class ) ); } return( NULL ); } void * model_save( Model *model, xmlNode *xnode ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_save_test( model ) ) { if( model_class->save && !model_class->save( model, xnode ) ) return( model ); } return( NULL ); } gboolean model_save_test( Model *model ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_class->save_test ) return( model_class->save_test( model ) ); return( TRUE ); } void * model_save_text( Model *model, iOpenFile *of ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_class->save_text && !model_class->save_text( model, of ) ) return( model ); return( NULL ); } void * model_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_class->load ) { if( !model_class->load( model, state, parent, xnode ) ) return( model ); } else { error_top( _( "Not implemented." ) ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "load", G_OBJECT_CLASS_NAME( model_class ) ); } return( NULL ); } void * model_load_text( Model *model, Model *parent, iOpenFile *of ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_class->load_text ) { if( !model_class->load_text( model, parent, of ) ) return( model ); } else { error_top( "Not implemented." ); error_sub( _( "_%s() not implemented for class \"%s\"." ), "load_text", G_OBJECT_CLASS_NAME( model_class ) ); } return( NULL ); } void * model_empty( Model *model ) { ModelClass *model_class = MODEL_GET_CLASS( model ); if( model_class->empty ) model_class->empty( model ); return( NULL ); } static void model_real_scrollto( Model *model, ModelScrollPosition position ) { } static void model_real_front( Model *model ) { } static void model_real_display( Model *model, gboolean display ) { if( display != model->display ) { model->display = display; iobject_changed( IOBJECT( model ) ); } } static xmlNode * model_real_save( Model *model, xmlNode *xnode ) { const char *tname = G_OBJECT_TYPE_NAME( model ); xmlNode *xthis; if( !(xthis = xmlNewChild( xnode, NULL, (xmlChar *) tname, NULL )) ) { error_top( _( "XML library error." ) ); error_sub( _( "model_save: xmlNewChild() failed" ) ); return( NULL ); } if( icontainer_map( ICONTAINER( model ), (icontainer_map_fn) model_save, xthis, NULL ) ) return( NULL ); if( model->window_width != -1 ) { if( !set_iprop( xthis, "window_x", model->window_x ) || !set_iprop( xthis, "window_y", model->window_y ) || !set_iprop( xthis, "window_width", model->window_width ) || !set_iprop( xthis, "window_height", model->window_height ) ) return( NULL ); } return( xthis ); } static void * model_new_xml_sub( ModelClass *model_class, ModelLoadState *state, Model *parent, xmlNode *xnode ) { GtkType type = GTK_CLASS_TYPE( model_class ); const char *tname = gtk_type_name( type ); if( strcasecmp( (char *) xnode->name, tname ) == 0 ) { Model *model = MODEL( g_object_new( type, NULL ) ); if( model_load( model, state, parent, xnode ) ) { g_object_unref( model ); return( model_class ); } return( NULL ); } return( NULL ); } gboolean model_new_xml( ModelLoadState *state, Model *parent, xmlNode *xnode ) { /* FIXME ... slow! some sort of hash? time this at some point */ if( slist_map3( model_registered_loadable, (SListMap3Fn) model_new_xml_sub, state, parent, xnode ) ) return( FALSE ); return( TRUE ); } static gboolean model_real_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { const char *tname = G_OBJECT_TYPE_NAME( model ); xmlNode *i; /* Should just be a sanity check. */ if( strcasecmp( (char *) xnode->name, tname ) != 0 ) { error_top( _( "XML load error." ) ); error_sub( _( "Can't load node of type \"%s\" into " "object of type \"%s\"" ), xnode->name, tname ); return( FALSE ); } (void) get_iprop( xnode, "window_x", &model->window_x ); (void) get_iprop( xnode, "window_y", &model->window_y ); (void) get_iprop( xnode, "window_width", &model->window_width ); (void) get_iprop( xnode, "window_height", &model->window_height ); if( !ICONTAINER( model )->parent ) icontainer_child_add( ICONTAINER( parent ), ICONTAINER( model ), -1 ); for( i = xnode->children; i; i = i->next ) if( !model_new_xml( state, MODEL( model ), i ) ) return( FALSE ); #ifdef DEBUG printf( "model_real_load: finished loading %s (name = %s)\n", tname, NN( IOBJECT( model )->name ) ); #endif /*DEBUG*/ return( TRUE ); } static void model_real_empty( Model *model ) { icontainer_map( ICONTAINER( model ), (icontainer_map_fn) icontainer_child_remove, NULL, NULL ); } static void model_class_init( ModelClass *class ) { iObjectClass *object_class = IOBJECT_CLASS( class ); parent_class = g_type_class_peek_parent( class ); class->view_new = NULL; class->edit = NULL; class->scrollto = model_real_scrollto; class->layout = NULL; class->front = model_real_front; class->display = model_real_display; class->reset = NULL; class->save = model_real_save; class->save_test = NULL; class->save_text = NULL; class->load = model_real_load; class->load_text = NULL; class->empty = model_real_empty; /* Create signals. */ model_signals[SIG_SCROLLTO] = g_signal_new( "scrollto", G_OBJECT_CLASS_TYPE( object_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ModelClass, scrollto ), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT ); model_signals[SIG_LAYOUT] = g_signal_new( "layout", G_OBJECT_CLASS_TYPE( object_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ModelClass, layout ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); model_signals[SIG_FRONT] = g_signal_new( "front", G_OBJECT_CLASS_TYPE( object_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ModelClass, front ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); model_signals[SIG_RESET] = g_signal_new( "reset", G_OBJECT_CLASS_TYPE( object_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ModelClass, reset ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); model_signals[SIG_DISPLAY] = g_signal_new( "display", G_OBJECT_CLASS_TYPE( object_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ModelClass, display ), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN ); } static void model_init( Model *model ) { model->display = TRUE; /* Magic: -1 means none of these saved settings are valid. It'd be * nice to do something better, but we'd break old workspaces. */ model->window_x = 0; model->window_y = 0; model->window_width = -1; model->window_height = 0; } GType model_get_type( void ) { static GType model_type = 0; if( !model_type ) { static const GTypeInfo info = { sizeof( ModelClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) model_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Model ), 32, /* n_preallocs */ (GInstanceInitFunc) model_init, }; model_type = g_type_register_static( TYPE_ICONTAINER, "Model", &info, 0 ); } return( model_type ); } void model_base_init( void ) { model_base = MODEL( g_object_new( TYPE_MODEL, NULL ) ); /* We have to init some of our other classes to get them registered * with the class loader. */ (void) g_type_class_ref( TYPE_CLOCK ); (void) g_type_class_ref( TYPE_COLOUR ); (void) g_type_class_ref( TYPE_EXPRESSION ); (void) g_type_class_ref( TYPE_FONTNAME ); (void) g_type_class_ref( TYPE_GROUP ); (void) g_type_class_ref( TYPE_IARROW ); (void) g_type_class_ref( TYPE_IIMAGE ); (void) g_type_class_ref( TYPE_IREGION ); (void) g_type_class_ref( TYPE_ITEXT ); (void) g_type_class_ref( TYPE_MATRIX ); (void) g_type_class_ref( TYPE_NUMBER ); (void) g_type_class_ref( TYPE_OPTION ); (void) g_type_class_ref( TYPE_PATHNAME ); (void) g_type_class_ref( TYPE_PLOT ); (void) g_type_class_ref( TYPE_REAL ); (void) g_type_class_ref( TYPE_SLIDER ); (void) g_type_class_ref( TYPE_STRING ); (void) g_type_class_ref( TYPE_TOGGLE ); (void) g_type_class_ref( TYPE_VECTOR ); (void) g_type_class_ref( TYPE_RHS ); (void) g_type_class_ref( TYPE_ROW ); (void) g_type_class_ref( TYPE_SUBCOLUMN ); (void) g_type_class_ref( TYPE_WORKSPACE ); (void) g_type_class_ref( TYPE_COLUMN ); } typedef struct { iDialog *idlg; /* The yesno we run */ Model *model; /* The model we watch */ guint destroy_sid; /* sid for the destroy */ iWindowFn done_cb; /* Call this at the end */ } ModelCheckDestroy; /* OK to destroy. */ static void model_check_destroy_sub( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { ModelCheckDestroy *mcd = (ModelCheckDestroy *) client; mcd->idlg = NULL; IDESTROY( mcd->model ); symbol_recalculate_all(); mcd->done_cb( iwnd, NULL, nfn, sys ); } /* The model we are watching has been killed, maybe by us. */ static void model_check_destroy_destroy_cb( Model *model, ModelCheckDestroy *mcd ) { g_assert( IS_MODEL( model ) ); g_assert( IS_MODEL( mcd->model ) ); g_assert( !mcd->idlg || IS_IDIALOG( mcd->idlg ) ); mcd->model = NULL; mcd->destroy_sid = 0; if( mcd->idlg ) { iWindow *iwnd = IWINDOW( mcd->idlg ); mcd->idlg = NULL; iwindow_kill( iwnd ); } } /* Our dialog is done. */ static void model_check_destroy_finished( void *client, iWindowResult result ) { ModelCheckDestroy *mcd = (ModelCheckDestroy *) client; FREESID( mcd->destroy_sid, mcd->model ); IM_FREE( mcd ); } void model_check_destroy( GtkWidget *parent, Model *model, iWindowFn done_cb ) { char txt[30]; VipsBuf buf = VIPS_BUF_STATIC( txt ); const char *name; ModelCheckDestroy *mcd = INEW( NULL, ModelCheckDestroy ); mcd->idlg = NULL; mcd->model = model; mcd->done_cb = done_cb ? done_cb : iwindow_true_cb; if( IS_SYMBOL( model ) ) { symbol_qualified_name( SYMBOL( model ), &buf ); name = vips_buf_all( &buf ); } else name = IOBJECT( model )->name; mcd->idlg = box_yesno( parent, model_check_destroy_sub, iwindow_true_cb, mcd, model_check_destroy_finished, mcd, GTK_STOCK_DELETE, _( "Delete?" ), _( "Are you sure you want to delete %s \"%s\"?" ), IOBJECT_GET_CLASS_NAME( model ), name ); /* In case someone else kills this model before we do. */ mcd->destroy_sid = g_signal_connect( model, "destroy", G_CALLBACK( model_check_destroy_destroy_cb ), mcd ); } /* Useful for icontainer_map_all() ... trigger all heapmodel_clear_edited() * methods. */ void * model_clear_edited( Model *model ) { void *result; if( IS_HEAPMODEL( model ) && (result = heapmodel_clear_edited( HEAPMODEL( model ) )) ) return( result ); return( NULL ); } ================================================ FILE: src/model.h ================================================ /* abstract base class for things which form the model of a model/view pair */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* When scrolling, do we want the top or the bottom of the object visible. * Important for Columns, since we sometimes want to see the title bar and * sometimes the edit box at the bottom. */ typedef enum { MODEL_SCROLL_TOP, MODEL_SCROLL_BOTTOM } ModelScrollPosition; /* How to rename symbols. */ typedef struct _ModelRename { char *old_name; char *new_name; } ModelRename; /* What we track during a load operation. */ typedef struct _ModelLoadState { char *filename; /* Name we loaded from */ char *filename_user; /* The filename to record in the model */ xmlDoc *xdoc; /* Document we load from */ /* FIXME ... a linked list? try a hash sometime see model_loadstate_rewrite_name() would probably only see a speedup for merging very large workspaces, not something we do often */ GSList *renames; /* Rename table for this load context */ /* The column renames we have planned. Don't rewrite exprs with these. */ GSList *column_renames; /* Version info we read from this XML file. */ int major; int minor; int micro; /* Log error messages here. */ char error_log_buffer[MAX_STRSIZE]; VipsBuf error_log; /* Set this bool to rewrite string constants using the filename * rewrite system. */ gboolean rewrite_path; /* The old_dir we added with path_rewrite_add() ... if non, NULL, * unset this rewrite rule on close. */ char *old_dir; } ModelLoadState; #define TYPE_MODEL (model_get_type()) #define MODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_MODEL, Model )) #define MODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_MODEL, ModelClass)) #define IS_MODEL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_MODEL )) #define IS_MODEL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_MODEL )) #define MODEL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_MODEL, ModelClass )) struct _Model { iContainer parent_object; /* My instance vars. */ gboolean display; /* This model should have a view */ /* For things that have a pop-up window (eg. iimage, plot), the * position and size of the window. */ int window_x, window_y; int window_width, window_height; }; typedef struct _ModelClass { iContainerClass parent_class; /* Build display methods. view_new make a view for this model ... make the top view yourself, thereafter view will watch child_add etc. and manage subviews automatically ... use model->display to create and destroy views */ View *(*view_new)( Model *model, View *parent ); /* Change methods edit open an editor on the model header view model header scrollto try to make views visible reset signals views to reset ... eg. textview pops back to whatever the ws says it should be displaying (value or formula) layout try to lay child view out front trigger view_child_front() for all views display create and destroy views */ void (*edit)( GtkWidget *, Model * ); void (*header)( GtkWidget *, Model * ); void (*scrollto)( Model *, ModelScrollPosition ); void (*reset)( Model * ); void (*layout)( Model * ); void (*front)( Model * ); void (*display)( Model *, gboolean display ); /* Load and save methods. save write model as child of node save_test predicate ... save model if save_test is defined and true save_text plain text save ... eg. for toolkits load _init() model from xmlNode load_text _init() from plain text ... eg. toolkit empty remove contents of model */ xmlNode *(*save)( Model *, xmlNode * ); gboolean (*save_test)( Model * ); gboolean (*save_text)( Model *, iOpenFile * ); gboolean (*load)( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ); gboolean (*load_text)( Model *model, Model *parent, iOpenFile * ); void (*empty)( Model * ); } ModelClass; extern ModelLoadState *model_loadstate; gboolean model_loadstate_rename_new( ModelLoadState *state, const char *old_name, const char *new_name ); gboolean model_loadstate_taken( ModelLoadState *state, const char *name ); gboolean model_loadstate_column_rename_new( ModelLoadState *state, const char *old_name, const char *new_name ); gboolean model_loadstate_column_taken( ModelLoadState *state, const char *name ); ModelLoadState *model_loadstate_new( const char *filename, const char *filename_user ); ModelLoadState *model_loadstate_new_openfile( iOpenFile *of ); void model_loadstate_destroy( ModelLoadState *state ); char *model_loadstate_rewrite_name( char *name ); void model_loadstate_rewrite( ModelLoadState *state, char *old_rhs, char *new_rhs ); void model_register_loadable( ModelClass *model_class ); View *model_view_new( Model *model, View *parent ); void model_scrollto( Model *model, ModelScrollPosition position ); void model_layout( Model *model ); void *model_reset( Model *model ); void *model_edit( GtkWidget *parent, Model *model ); void *model_header( GtkWidget *parent, Model *model ); void model_front( Model *model ); void model_display( Model *model, gboolean display ); void *model_save( Model *model, xmlNode * ); gboolean model_save_test( Model *model ); void *model_save_text( Model *model, iOpenFile * ); void *model_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ); void *model_load_text( Model *model, Model *parent, iOpenFile * ); void *model_empty( Model *model ); gboolean model_new_xml( ModelLoadState *state, Model *parent, xmlNode *xnode ); GType model_get_type( void ); void model_base_init( void ); View *model_build_display_all( Model *model, View *parent ); void model_check_destroy( GtkWidget *parent, Model *model, iWindowFn done_cb ); void *model_clear_edited( Model *model ); ================================================ FILE: src/nip2-cli.c ================================================ /* nip2-cli.c ... run the nip2 executable, connecting stdin and stdout to the * console * * 11/12/09 * - use SetHandleInformation() to stop the child inheriting the read * handle (thanks Leo) */ /* Copyright (C) 2008 Imperial College, London 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Adapted from sample code by Leo Davidson, with the author's permission. */ /* Windows does not let a single exe run in both command-line and GUI mode. To * run nip2 in command-line mode, we run this CLI wrapper program instead, * which starts the main nip2 exe, connecting stdin/out/err appropriately. */ #include #include #include #include #include void print_last_error () { char *buf; if (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) & buf, 0, NULL)) { fprintf (stderr, "%s", buf); LocalFree (buf); } } int main (int argc, char **argv) { char *dirname; char command[2048]; gboolean quote; int i, j; HANDLE hChildStdoutRd; HANDLE hChildStdoutWr; SECURITY_ATTRIBUTES saAttr; PROCESS_INFORMATION processInformation; STARTUPINFO startUpInfo; DWORD dwRead; CHAR buf[1024]; /* we run the nip2.exe in the same directory as this exe: swap the last * pathname component for nip2.exe * we change the argv[0] pointer, probably not a good idea */ dirname = g_path_get_dirname (argv[0]); argv[0] = g_build_filename (dirname, "nip2.exe", NULL); g_free (dirname); if (_access (argv[0], 00)) { fprintf (stderr, "cannot access \"%s\"\n", argv[0]); exit (1); } /* build the command string ... we have to quote items containing spaces */ command[0] = '\0'; for (i = 0; i < argc; i++) { quote = FALSE; for (j = 0; argv[i][j]; j++) { if (isspace (argv[i][j])) { quote = TRUE; break; } } if (i > 0) { strncat (command, " ", sizeof (command) - 1); } if (quote) { strncat (command, "\"", sizeof (command) - 1); } strncat (command, argv[i], sizeof (command) - 1); if (quote) { strncat (command, "\"", sizeof (command) - 1); } } if (strlen (command) == sizeof (command) - 1) { fprintf (stderr, "command too long\n"); exit (1); } /* Create a pipe for the child process's STDOUT. */ hChildStdoutRd = NULL; hChildStdoutWr = NULL; saAttr.nLength = sizeof (SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe (&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) { fprintf (stderr, "CreatePipe failed: "); print_last_error (); fprintf (stderr, "\n"); exit (1); } /* Ensure the read handle to the pipe for STDOUT is not inherited. */ if (!SetHandleInformation(hChildStdoutRd, HANDLE_FLAG_INHERIT, 0)) { fprintf (stderr, "SetHandleInformation failed: "); print_last_error (); fprintf (stderr, "\n"); exit (1); } /* Run command. */ startUpInfo.cb = sizeof (STARTUPINFO); startUpInfo.lpReserved = NULL; startUpInfo.lpDesktop = NULL; startUpInfo.lpTitle = NULL; startUpInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; startUpInfo.hStdOutput = hChildStdoutWr; startUpInfo.hStdError = hChildStdoutWr; startUpInfo.cbReserved2 = 0; startUpInfo.lpReserved2 = NULL; startUpInfo.wShowWindow = SW_SHOWNORMAL; if (!CreateProcess (NULL, command, NULL, /* default security */ NULL, /* default thread security */ TRUE, /* inherit handles */ CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS, NULL, /* use default environment */ NULL, /* set default directory */ &startUpInfo, &processInformation)) { fprintf (stderr, "error running \"%s\": ", command); print_last_error (); fprintf (stderr, "\n"); exit (1); } /* Close the write end of the pipe before reading from the read end. */ CloseHandle (hChildStdoutWr); while (ReadFile (hChildStdoutRd, buf, sizeof (buf) - 1, &dwRead, NULL) && dwRead > 0) { buf[dwRead] = '\0'; printf ("%s", buf); } CloseHandle (hChildStdoutRd); return (0); } ================================================ FILE: src/nip2-icon.rc ================================================ 1 ICON "nip2-icon.ico" ================================================ FILE: src/nipmarshal.c ================================================ #include "nipmarshal.h" #include #ifdef G_ENABLE_DEBUG #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) #define g_marshal_value_peek_char(v) g_value_get_char (v) #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) #define g_marshal_value_peek_int(v) g_value_get_int (v) #define g_marshal_value_peek_uint(v) g_value_get_uint (v) #define g_marshal_value_peek_long(v) g_value_get_long (v) #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) #define g_marshal_value_peek_enum(v) g_value_get_enum (v) #define g_marshal_value_peek_flags(v) g_value_get_flags (v) #define g_marshal_value_peek_float(v) g_value_get_float (v) #define g_marshal_value_peek_double(v) g_value_get_double (v) #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) #define g_marshal_value_peek_param(v) g_value_get_param (v) #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) #define g_marshal_value_peek_object(v) g_value_get_object (v) #else /* !G_ENABLE_DEBUG */ /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. * Do not access GValues directly in your code. Instead, use the * g_value_get_*() functions */ #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int #define g_marshal_value_peek_char(v) (v)->data[0].v_int #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint #define g_marshal_value_peek_int(v) (v)->data[0].v_int #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint #define g_marshal_value_peek_long(v) (v)->data[0].v_long #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 #define g_marshal_value_peek_enum(v) (v)->data[0].v_long #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong #define g_marshal_value_peek_float(v) (v)->data[0].v_float #define g_marshal_value_peek_double(v) (v)->data[0].v_double #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer #endif /* !G_ENABLE_DEBUG */ /* VOID:OBJECT,INT (nipmarshal.list:25) */ void nip_VOID__OBJECT_INT (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__OBJECT_INT) (gpointer data1, gpointer arg_1, gint arg_2, gpointer data2); register GMarshalFunc_VOID__OBJECT_INT callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; g_return_if_fail (n_param_values == 3); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_VOID__OBJECT_INT) (marshal_data ? marshal_data : cc->callback); callback (data1, g_marshal_value_peek_object (param_values + 1), g_marshal_value_peek_int (param_values + 2), data2); } /* VOID:DOUBLE,DOUBLE (nipmarshal.list:26) */ void nip_VOID__DOUBLE_DOUBLE (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer data1, gdouble arg_1, gdouble arg_2, gpointer data2); register GMarshalFunc_VOID__DOUBLE_DOUBLE callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; g_return_if_fail (n_param_values == 3); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback); callback (data1, g_marshal_value_peek_double (param_values + 1), g_marshal_value_peek_double (param_values + 2), data2); } /* BOOLEAN:INT,INT (nipmarshal.list:27) */ void nip_BOOLEAN__INT_INT (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT) (gpointer data1, gint arg_1, gint arg_2, gpointer data2); register GMarshalFunc_BOOLEAN__INT_INT callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; gboolean v_return; g_return_if_fail (return_value != NULL); g_return_if_fail (n_param_values == 3); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_BOOLEAN__INT_INT) (marshal_data ? marshal_data : cc->callback); v_return = callback (data1, g_marshal_value_peek_int (param_values + 1), g_marshal_value_peek_int (param_values + 2), data2); g_value_set_boolean (return_value, v_return); } ================================================ FILE: src/nipmarshal.h ================================================ #ifndef __nip_MARSHAL_H__ #define __nip_MARSHAL_H__ #include G_BEGIN_DECLS /* VOID:OBJECT,INT (nipmarshal.list:25) */ extern void nip_VOID__OBJECT_INT (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); /* VOID:DOUBLE,DOUBLE (nipmarshal.list:26) */ extern void nip_VOID__DOUBLE_DOUBLE (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); /* BOOLEAN:INT,INT (nipmarshal.list:27) */ extern void nip_BOOLEAN__INT_INT (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); G_END_DECLS #endif /* __nip_MARSHAL_H__ */ ================================================ FILE: src/nipmarshal.list ================================================ # see glib-genmarshal(1) for a detailed description of the file format, # possible parameter types are: # VOID indicates no return type, or no extra # parameters. if VOID is used as the parameter # list, no additional parameters may be present. # BOOLEAN for boolean types (gboolean) # CHAR for signed char types (gchar) # UCHAR for unsigned char types (guchar) # INT for signed integer types (gint) # UINT for unsigned integer types (guint) # LONG for signed long integer types (glong) # ULONG for unsigned long integer types (gulong) # ENUM for enumeration types (gint) # FLAGS for flag enumeration types (guint) # FLOAT for single-precision float types (gfloat) # DOUBLE for double-precision float types (gdouble) # STRING for string types (gchar*) # BOXED for boxed (anonymous but reference counted) types (GBoxed*) # POINTER for anonymous pointer types (gpointer) # PARAM for GParamSpec or derived types (GParamSpec*) # OBJECT for GObject or derived types (GObject*) # NONE deprecated alias for VOID # BOOL deprecated alias for BOOLEAN VOID: OBJECT, INT VOID: DOUBLE, DOUBLE BOOLEAN: INT, INT ================================================ FILE: src/number.c ================================================ /* an editable number */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static View * number_view_new( Model *model, View *parent ) { return( numberview_new() ); } /* Members of number we automate. */ static ClassmodelMember number_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( iObject, caption ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Number, value ) } }; static void number_class_init( NumberClass *class ) { iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Init methods. */ iobject_class->user_name = _( "Number" ); model_class->view_new = number_view_new; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = number_members; classmodel_class->n_members = IM_NUMBER( number_members ); } static void number_init( Number *number ) { number->value = 0.0; iobject_set( IOBJECT( number ), CLASS_NUMBER, NULL ); } GType number_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( NumberClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) number_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Number ), 32, /* n_pnumberlocs */ (GInstanceInitFunc) number_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Number", &info, 0 ); } return( type ); } ================================================ FILE: src/number.h ================================================ /* a colour number in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_NUMBER (number_get_type()) #define NUMBER( obj ) (GTK_CHECK_CAST( (obj), TYPE_NUMBER, Number )) #define NUMBER_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_NUMBER, NumberClass )) #define IS_NUMBER( obj ) (GTK_CHECK_TYPE( (obj), TYPE_NUMBER )) #define IS_NUMBER_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_NUMBER )) struct _Number { Classmodel parent_class; /* Class fields. */ double value; }; typedef struct _NumberClass { ClassmodelClass parent_class; /* My methods. */ } NumberClass; GType number_get_type( void ); ================================================ FILE: src/numberview.c ================================================ /* a view of a text thingy */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static EditviewClass *parent_class = NULL; /* Re-read the text in a tally entry. */ static void * numberview_scan( View *view ) { Numberview *numberview = NUMBERVIEW( view ); Number *number = NUMBER( VOBJECT( numberview )->iobject ); Expr *expr = HEAPMODEL( number )->row->expr; double value; #ifdef DEBUG Row *row = HEAPMODEL( number )->row; printf( "numberview_scan: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ expr_error_clear( expr ); if( !get_geditable_double( EDITVIEW( numberview )->text, &value ) ) { expr_error_set( expr ); return( view ); } if( number->value != value ) { number->value = value; classmodel_update( CLASSMODEL( number ) ) ; } return( VIEW_CLASS( parent_class )->scan( view ) ); } static void numberview_refresh( vObject *vobject ) { Numberview *numberview = NUMBERVIEW( vobject ); Number *number = NUMBER( VOBJECT( numberview )->iobject ); #ifdef DEBUG Row *row = HEAPMODEL( number )->row; printf( "numberview_refresh: " ); row_name_print( row ); printf( " (%p)\n", vobject ); #endif /*DEBUG*/ editview_set_entry( EDITVIEW( numberview ), "%g", number->value ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void numberview_class_init( NumberviewClass *class ) { ViewClass *view_class = (ViewClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = numberview_refresh; view_class->scan = numberview_scan; } static void numberview_init( Numberview *numberview ) { } GtkType numberview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Numberview", sizeof( Numberview ), sizeof( NumberviewClass ), (GtkClassInitFunc) numberview_class_init, (GtkObjectInitFunc) numberview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_EDITVIEW, &info ); } return( type ); } View * numberview_new( void ) { Numberview *numberview = gtk_type_new( TYPE_NUMBERVIEW ); return( VIEW( numberview ) ); } ================================================ FILE: src/numberview.h ================================================ /* edit a number */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_NUMBERVIEW (numberview_get_type()) #define NUMBERVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_NUMBERVIEW, Numberview )) #define NUMBERVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_NUMBERVIEW, NumberviewClass )) #define IS_NUMBERVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_NUMBERVIEW )) #define IS_NUMBERVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_NUMBERVIEW )) typedef struct _Numberview { Editview parent_object; } Numberview; typedef struct _NumberviewClass { EditviewClass parent_class; /* My methods. */ } NumberviewClass; GtkType numberview_get_type( void ); View *numberview_new( void ); ================================================ FILE: src/option.c ================================================ /* an input option ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void option_finalize( GObject *gobject ) { Option *option; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_OPTION( gobject ) ); option = OPTION( gobject ); /* My instance finalize stuff. */ IM_FREEF( slist_free_all, option->labels ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static View * option_view_new( Model *model, View *parent ) { return( optionview_new() ); } /* Members of option we automate. */ static ClassmodelMember option_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( iObject, caption ) }, { CLASSMODEL_MEMBER_STRING_LIST, NULL, 0, MEMBER_LABELS, "labels", N_( "Labels" ), G_STRUCT_OFFSET( Option, labels ) }, { CLASSMODEL_MEMBER_INT, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Option, value ) } }; static void option_class_init( OptionClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = option_finalize; model_class->view_new = option_view_new; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = option_members; classmodel_class->n_members = IM_NUMBER( option_members ); } static void option_init( Option *option ) { option->labels = NULL; option->value = 0; iobject_set( IOBJECT( option ), CLASS_OPTION, NULL ); } GType option_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( OptionClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) option_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Option ), 32, /* n_preallocs */ (GInstanceInitFunc) option_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Option", &info, 0 ); } return( type ); } ================================================ FILE: src/option.h ================================================ /* a option in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_OPTION (option_get_type()) #define OPTION( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_OPTION, Option )) #define OPTION_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_OPTION, OptionClass)) #define IS_OPTION( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_OPTION )) #define IS_OPTION_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_OPTION )) #define OPTION_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_OPTION, OptionClass )) typedef struct _Option { Classmodel parent_class; /* Base class fields. */ GSList *labels; /* [[char]] for option fields */ int value; /* Index of current option */ } Option; typedef struct _OptionClass { ClassmodelClass parent_class; /* My methods. */ } OptionClass; GType option_get_type( void ); ================================================ FILE: src/optionview.c ================================================ /* run the display for a option in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; /* Copy a gslist of strings. */ static GSList * lstring_copy( GSList *lstring ) { GSList *new; GSList *p; new = NULL; for( p = lstring; p; p = p->next ) new = g_slist_prepend( new, g_strdup( (const char *) p->data ) ); new = g_slist_reverse( new ); return( new ); } /* Are two lstrings equal? */ static gboolean lstring_equal( GSList *a, GSList *b ) { for( ; a && b; a = a->next, b = b->next ) if( strcmp( (const char *) a->data, (const char *) b->data ) != 0 ) return( FALSE ); if( a || b ) return( FALSE ); return( TRUE ); } static void optionview_destroy( GtkObject *object ) { Optionview *optionview; g_return_if_fail( object != NULL ); g_return_if_fail( IS_OPTIONVIEW( object ) ); optionview = OPTIONVIEW( object ); /* My instance destroy stuff. */ IM_FREEF( slist_free_all, optionview->labels ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void optionview_link( View *view, Model *model, View *parent ) { Optionview *optionview = OPTIONVIEW( view ); VIEW_CLASS( parent_class )->link( view, model, parent ); if( GRAPHICVIEW( view )->sview ) gtk_size_group_add_widget( GRAPHICVIEW( view )->sview->group, optionview->label ); } /* Change to a optionview widget ... update the model. */ static void optionview_change_cb( GtkWidget *wid, Optionview *optionview ) { Option *option = OPTION( VOBJECT( optionview )->iobject ); Classmodel *classmodel = CLASSMODEL( option ); const int nvalue = gtk_combo_box_get_active( GTK_COMBO_BOX( optionview->options ) ); if( option->value != nvalue ) { option->value = nvalue; classmodel_update( classmodel ); symbol_recalculate_all(); } } static gboolean optionview_scroll_cb( GtkWidget *wid, GdkEvent *event, Optionview *optionview ) { /* Stop any other scroll handlers running. We don't want the scroll * wheel to change widgets while we're moving. */ return( TRUE ); } static void optionview_refresh( vObject *vobject ) { Optionview *optionview = OPTIONVIEW( vobject ); Option *option = OPTION( VOBJECT( optionview )->iobject ); GSList *p; int i; #ifdef DEBUG printf( "optionview_refresh: " ); row_name_print( HEAPMODEL( option )->row ); printf( "\n" ); #endif /*DEBUG*/ /* Only rebuild the menu if there's been a change. */ if( !lstring_equal( optionview->labels, option->labels ) ) { /* If the menu is currently up, we can get strange things * happening if we destroy it. */ if( optionview->options ) gtk_combo_box_popdown( GTK_COMBO_BOX( optionview->options ) ); IM_FREEF( gtk_widget_destroy, optionview->options ); optionview->options = gtk_combo_box_new_text(); for( p = option->labels, i = 0; p; p = p->next, i++ ) gtk_combo_box_append_text( GTK_COMBO_BOX( optionview->options ), (const char *) p->data ); gtk_box_pack_start( GTK_BOX( optionview->hbox ), optionview->options, TRUE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( optionview->options ), "changed", GTK_SIGNAL_FUNC( optionview_change_cb ), optionview ); gtk_widget_show( optionview->options ); IM_FREEF( slist_free_all, optionview->labels ); optionview->labels = lstring_copy( option->labels ); g_signal_connect( GTK_OBJECT( optionview->options ), "scroll-event", GTK_SIGNAL_FUNC( optionview_scroll_cb ), optionview ); } if( optionview->options ) { gtk_signal_handler_block_by_data( GTK_OBJECT( optionview->options ), optionview ); gtk_combo_box_set_active( GTK_COMBO_BOX( optionview->options ), option->value ); gtk_signal_handler_unblock_by_data( GTK_OBJECT( optionview->options ), optionview ); } set_glabel( optionview->label, _( "%s:" ), IOBJECT( option )->caption ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void optionview_class_init( OptionviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = optionview_destroy; /* Create signals. */ /* Init methods. */ vobject_class->refresh = optionview_refresh; view_class->link = optionview_link; } static void optionview_init( Optionview *optionview ) { optionview->hbox = gtk_hbox_new( FALSE, 12 ); gtk_box_pack_start( GTK_BOX( optionview ), optionview->hbox, TRUE, FALSE, 0 ); optionview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( optionview->label ), 0, 0.5 ); gtk_box_pack_start( GTK_BOX( optionview->hbox ), optionview->label, FALSE, FALSE, 2 ); optionview->options = NULL; optionview->labels = NULL; gtk_widget_show_all( optionview->hbox ); } GtkType optionview_get_type( void ) { static GtkType optionview_type = 0; if( !optionview_type ) { static const GtkTypeInfo sinfo = { "Optionview", sizeof( Optionview ), sizeof( OptionviewClass ), (GtkClassInitFunc) optionview_class_init, (GtkObjectInitFunc) optionview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; optionview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &sinfo ); } return( optionview_type ); } View * optionview_new( void ) { Optionview *optionview = gtk_type_new( TYPE_OPTIONVIEW ); return( VIEW( optionview ) ); } ================================================ FILE: src/optionview.h ================================================ /* a optionview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_OPTIONVIEW (optionview_get_type()) #define OPTIONVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_OPTIONVIEW, Optionview )) #define OPTIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_OPTIONVIEW, OptionviewClass )) #define IS_OPTIONVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_OPTIONVIEW )) #define IS_OPTIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_OPTIONVIEW )) typedef struct _Optionview { Graphicview parent_object; GtkWidget *label; GtkWidget *hbox; GtkWidget *options; /* The [[char]] we set on the previous refresh. Use this to avoid * rebuilding the optionmenu unless we have to. */ GSList *labels; } Optionview; typedef struct _OptionviewClass { GraphicviewClass parent_class; /* My methods. */ } OptionviewClass; GtkType optionview_get_type( void ); View *optionview_new( void ); ================================================ FILE: src/paintboxview.c ================================================ /* widgets for the paintbox bar */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GtkFrameClass *parent_class = NULL; /* The popup menu. */ static GtkWidget *paintboxview_menu = NULL; static void paintboxview_destroy( GtkObject *object ) { Paintboxview *pbv; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PAINTBOXVIEW( object ) ); pbv = PAINTBOXVIEW( object ); #ifdef DEBUG printf( "paintboxview_destroy: %p\n", pbv ); #endif /*DEBUG*/ /* My instance destroy stuff. */ FREESID( pbv->ii_undo_changed_sid, pbv->ii ); FREESID( pbv->ii_destroy_sid, pbv->ii ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void paintboxview_realize( GtkWidget *widget ) { Paintboxview *pbv = PAINTBOXVIEW( widget ); iWindow *iwnd = IWINDOW( iwindow_get_root( widget ) ); guint key; GdkModifierType mods; gtk_accelerator_parse( "z", &key, &mods ); gtk_widget_add_accelerator( GTK_WIDGET( pbv->undo ), "clicked", iwnd->accel_group, key, mods, 0 ); gtk_accelerator_parse( "z", &key, &mods ); gtk_widget_add_accelerator( GTK_WIDGET( pbv->redo ), "clicked", iwnd->accel_group, key, mods, 0 ); GTK_WIDGET_CLASS( parent_class )->realize( widget ); } /* Hide this paintboxview. */ static void paintboxview_hide_cb( GtkWidget *menu, GtkWidget *host, Paintboxview *pbv ) { imagemodel_set_paintbox( pbv->imagemodel, FALSE ); } static void paintboxview_class_init( PaintboxviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; GtkWidget *pane; parent_class = g_type_class_peek_parent( class ); object_class->destroy = paintboxview_destroy; widget_class->realize = paintboxview_realize; /* Create signals. */ /* Init methods. */ pane = paintboxview_menu = popup_build( _( "Paintbox bar menu" ) ); popup_add_but( pane, GTK_STOCK_CLOSE, POPUP_FUNC( paintboxview_hide_cb ) ); } /* "toggled" on a tool select button */ static void paintboxview_tool_toggled_cb( GtkWidget *wid, Paintboxview *pbv ) { if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( wid ) ) ) { Imagemodel *imagemodel = pbv->imagemodel; int i; for( i = 0; i < IMAGEMODEL_LAST; i++ ) if( wid == pbv->tool[i] ) break; if( i != (int) IMAGEMODEL_LAST ) imagemodel_set_state( imagemodel, i, wid ); } } /* New nib selected. */ static void paintboxview_scale_change_cb( Tslider *tslider, Paintboxview *pbv ) { Imagemodel *imagemodel = pbv->imagemodel; if( imagemodel->nib_radius != tslider->value ) { imagemodel->nib_radius = tslider->value; iobject_changed( IOBJECT( imagemodel ) ); } } static void paintboxview_double_cb( GtkWidget *wid, GdkEvent *event, Paintboxview *pbv ) { imageinfo_colour_edit( wid, IMAGEDISPLAY( pbv->ink )->conv->ii ); } static void paintboxview_font_changed_cb( GtkWidget *widget, Paintboxview *pbv ) { Fontbutton *fontbutton = FONTBUTTON( widget ); Imagemodel *imagemodel = pbv->imagemodel; const char *font_name; font_name = fontbutton_get_font_name( fontbutton ); if( strcmp( font_name, imagemodel->font_name ) != 0 ) { IM_SETSTR( imagemodel->font_name, font_name ); iobject_changed( IOBJECT( imagemodel ) ); } } static void paintboxview_undo_cb( GtkWidget *widget, Paintboxview *pbv ) { if( !imageinfo_undo( pbv->ii ) ) iwindow_alert( widget, GTK_MESSAGE_ERROR ); /* Ask everyone to drop cache, the image has changed. */ im_invalidate( imageinfo_get( FALSE, pbv->ii ) ); imagemodel_paint_recalc( pbv->imagemodel ); } static void paintboxview_redo_cb( GtkWidget *widget, Paintboxview *pbv ) { if( !imageinfo_redo( pbv->ii ) ) iwindow_alert( widget, GTK_MESSAGE_ERROR ); /* Ask everyone to drop cache, the image has changed. */ im_invalidate( imageinfo_get( FALSE, pbv->ii ) ); imagemodel_paint_recalc( pbv->imagemodel ); } static void paintboxview_clear_cb2( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Paintboxview *pbv = PAINTBOXVIEW( client ); imageinfo_undo_clear( pbv->ii ); nfn( sys, IWINDOW_YES ); } static void paintboxview_clear_cb( GtkWidget *widget, Paintboxview *pbv ) { box_yesno( GTK_WIDGET( widget ), paintboxview_clear_cb2, iwindow_true_cb, pbv, iwindow_notify_null, NULL, GTK_STOCK_CLEAR, _( "Clear undo history?" ), _( "Are you sure you want to clear all undo and redo? " "This will free up memory, but you will no longer be " "able to undo or redo any of the painting you have " "done so far." ) ); } static void paintboxview_text_changed_cb( GtkWidget *widget, Paintboxview *pbv ) { const char *text = gtk_entry_get_text( GTK_ENTRY( pbv->text ) ); IM_SETSTR( pbv->imagemodel->text, text ); } static void paintboxview_init( Paintboxview *pbv ) { /* Order important! Keep in sync with ImagemodelState. */ static const char *tool_names[IMAGEMODEL_LAST] = { STOCK_SELECT, /* IMAGEMODEL_SELECT */ STOCK_MOVE, /* IMAGEMODEL_PAN */ GTK_STOCK_ZOOM_IN, /* IMAGEMODEL_MAGIN */ GTK_STOCK_ZOOM_OUT, /* IMAGEMODEL_MAGOUT*/ STOCK_DROPPER, /* IMAGEMODEL_DROPPER */ STOCK_PAINTBRUSH, /* IMAGEMODEL_PEN */ STOCK_LINE, /* IMAGEMODEL_LINE */ STOCK_RECT, /* IMAGEMODEL_RECT */ STOCK_FLOOD, /* IMAGEMODEL_FLOOD */ STOCK_FLOOD_BLOB, /* IMAGEMODEL_BLOB */ STOCK_TEXT, /* IMAGEMODEL_TEXT */ STOCK_SMUDGE /* IMAGEMODEL_SMUDGE */ }; static const char *tool_tooltips[] = { N_( "Manipulate regions" ), /* IMAGEMODEL_SELECT */ N_( "Pan window" ), /* IMAGEMODEL_PAN */ N_( "Zoom in on mouse" ), /* IMAGEMODEL_MAGIN */ N_( "Zoom out" ), /* IMAGEMODEL_MAGOUT*/ N_( "Read pixel into inkwell" ), /* IMAGEMODEL_DROPPER */ N_( "Freehand draw " ), /* IMAGEMODEL_PEN */ N_( "Draw straight lines" ), /* IMAGEMODEL_LINE */ N_( "Fill rectangles" ), /* IMAGEMODEL_RECT */ N_( "Flood while pixel not equal to ink" ), /* IMAGEMODEL_FLOOD */ N_( "Flood while pixel equal to click" ), /* IMAGEMODEL_BLOB */ N_( "Draw text" ), /* IMAGEMODEL_TEXT */ N_( "Smudge" ) /* IMAGEMODEL_SMUDGE */ }; GtkWidget *eb; GtkWidget *hb, *hb2; GtkWidget *image; int i; pbv->imagemodel = NULL; pbv->ii_undo_changed_sid = 0; pbv->ii_destroy_sid = 0; pbv->ii = NULL; gtk_frame_set_shadow_type( GTK_FRAME( pbv ), GTK_SHADOW_OUT ); eb = gtk_event_box_new(); gtk_container_add( GTK_CONTAINER( pbv ), eb ); popup_attach( eb, paintboxview_menu, pbv ); hb = gtk_hbox_new( FALSE, 4 ); gtk_container_set_border_width( GTK_CONTAINER( hb ), 1 ); gtk_container_add( GTK_CONTAINER( eb ), hb ); /* The first 4 tools are harmless (region, move, zoom in, zoom out) * and not linked to the paint actions .. so have them first on their * own. */ hb2 = gtk_hbox_new( FALSE, 0 ); for( i = 0; i < 4; i++ ) { pbv->tool[i] = gtk_toggle_button_new(); gtk_signal_connect( GTK_OBJECT( pbv->tool[i] ), "toggled", GTK_SIGNAL_FUNC( paintboxview_tool_toggled_cb ), pbv ); image = gtk_image_new_from_stock( tool_names[i], GTK_ICON_SIZE_BUTTON ); set_tooltip( pbv->tool[i], "%s", tool_tooltips[i] ); gtk_container_add( GTK_CONTAINER( pbv->tool[i] ), image ); gtk_box_pack_start( GTK_BOX( hb2 ), pbv->tool[i], FALSE, FALSE, 0 ); } gtk_box_pack_start( GTK_BOX( hb ), hb2, FALSE, FALSE, 0 ); hb2 = gtk_hbox_new( FALSE, 0 ); pbv->undo = gtk_button_new(); image = gtk_image_new_from_stock( GTK_STOCK_UNDO, GTK_ICON_SIZE_BUTTON ); gtk_container_add( GTK_CONTAINER( pbv->undo ), image ); gtk_signal_connect( GTK_OBJECT( pbv->undo ), "clicked", GTK_SIGNAL_FUNC( paintboxview_undo_cb ), pbv ); set_tooltip( pbv->undo, _( "Undo last paint action" ) ); gtk_box_pack_start( GTK_BOX( hb2 ), pbv->undo, FALSE, FALSE, 0 ); pbv->redo = gtk_button_new(); image = gtk_image_new_from_stock( GTK_STOCK_REDO, GTK_ICON_SIZE_BUTTON ); gtk_container_add( GTK_CONTAINER( pbv->redo ), image ); gtk_signal_connect( GTK_OBJECT( pbv->redo ), "clicked", GTK_SIGNAL_FUNC( paintboxview_redo_cb ), pbv ); set_tooltip( pbv->redo, _( "Redo last paint action" ) ); gtk_box_pack_start( GTK_BOX( hb2 ), pbv->redo, FALSE, FALSE, 0 ); pbv->clear = gtk_button_new(); image = gtk_image_new_from_stock( GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON ); gtk_container_add( GTK_CONTAINER( pbv->clear ), image ); gtk_signal_connect( GTK_OBJECT( pbv->clear ), "clicked", GTK_SIGNAL_FUNC( paintboxview_clear_cb ), pbv ); set_tooltip( pbv->clear, _( "Clear all undo and redo buffers" ) ); gtk_box_pack_start( GTK_BOX( hb2 ), pbv->clear, FALSE, FALSE, 0 ); gtk_box_pack_start( GTK_BOX( hb ), hb2, FALSE, FALSE, 0 ); hb2 = gtk_hbox_new( FALSE, 0 ); for( i = 4; i < IM_NUMBER( tool_names ); i++ ) { pbv->tool[i] = gtk_toggle_button_new(); gtk_signal_connect( GTK_OBJECT( pbv->tool[i] ), "toggled", GTK_SIGNAL_FUNC( paintboxview_tool_toggled_cb ), pbv ); image = gtk_image_new_from_stock( tool_names[i], GTK_ICON_SIZE_BUTTON ); set_tooltip( pbv->tool[i], "%s", tool_tooltips[i] ); gtk_container_add( GTK_CONTAINER( pbv->tool[i] ), image ); gtk_box_pack_start( GTK_BOX( hb2 ), pbv->tool[i], FALSE, FALSE, 0 ); } gtk_box_pack_start( GTK_BOX( hb ), hb2, FALSE, FALSE, 0 ); pbv->nib = tslider_new(); pbv->nib->from = 0; pbv->nib->to = 64; pbv->nib->value = 0; pbv->nib->svalue = 1; pbv->nib->digits = 2; tslider_changed( pbv->nib ); gtk_box_pack_start( GTK_BOX( hb ), GTK_WIDGET( pbv->nib ), FALSE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( pbv->nib ), "changed", GTK_SIGNAL_FUNC( paintboxview_scale_change_cb ), pbv ); tslider_set_ignore_scroll( pbv->nib, FALSE ); pbv->ink = (GtkWidget *) colourdisplay_new( NULL ); doubleclick_add( GTK_WIDGET( pbv->ink ), FALSE, NULL, NULL, DOUBLECLICK_FUNC( paintboxview_double_cb ), pbv ); gtk_widget_set_size_request( GTK_WIDGET( pbv->ink ), 20, 10 ); gtk_box_pack_start( GTK_BOX( hb ), pbv->ink, FALSE, TRUE, 0 ); pbv->font = GTK_WIDGET( fontbutton_new() ); gtk_box_pack_start( GTK_BOX( hb ), pbv->font, FALSE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( pbv->font ), "changed", GTK_SIGNAL_FUNC( paintboxview_font_changed_cb ), pbv ); pbv->text = gtk_entry_new(); gtk_box_pack_start( GTK_BOX( hb ), pbv->text, TRUE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( pbv->text ), "changed", GTK_SIGNAL_FUNC( paintboxview_text_changed_cb ), pbv ); set_tooltip( pbv->text, _( "Enter text for text tool" ) ); gtk_widget_show_all( eb ); } GtkType paintboxview_get_type( void ) { static GtkType paintboxview_type = 0; if( !paintboxview_type ) { static const GtkTypeInfo sinfo = { "Paintboxview", sizeof( Paintboxview ), sizeof( PaintboxviewClass ), (GtkClassInitFunc) paintboxview_class_init, (GtkObjectInitFunc) paintboxview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; paintboxview_type = gtk_type_unique( GTK_TYPE_FRAME, &sinfo ); } return( paintboxview_type ); } static void paintboxview_ii_undo_changed_cb( Imageinfo *imageinfo, Paintboxview *pbv ) { gtk_widget_set_sensitive( GTK_WIDGET( pbv->undo ), imageinfo->undo != NULL ); gtk_widget_set_sensitive( GTK_WIDGET( pbv->redo ), imageinfo->redo != NULL ); gtk_widget_set_sensitive( GTK_WIDGET( pbv->clear ), imageinfo->undo != NULL || imageinfo->redo != NULL ); } static void paintboxview_ii_destroy_cb( Imageinfo *imageinfo, Paintboxview *pbv ) { pbv->ii_destroy_sid = 0; pbv->ii_undo_changed_sid = 0; pbv->ii = NULL; } /* Our model has changed ... update. */ static void paintboxview_changed_cb( Imagemodel *imagemodel, Paintboxview *pbv ) { Conversion *conv = imagemodel->conv; Colourdisplay *ink = COLOURDISPLAY( pbv->ink ); int i; #ifdef DEBUG printf( "paintboxview_conv_changed_cb: %p\n", conv ); #endif /*DEBUG*/ /* Has the ii changed? Link to it for undo/redo changes. */ if( pbv->ii != conv->ii ) { FREESID( pbv->ii_undo_changed_sid, pbv->ii ); FREESID( pbv->ii_destroy_sid, pbv->ii ); pbv->ii = conv->ii; if( conv->ii ) { pbv->ii_undo_changed_sid = g_signal_connect( G_OBJECT( conv->ii ), "undo_changed", G_CALLBACK( paintboxview_ii_undo_changed_cb ), pbv ); pbv->ii_destroy_sid = g_signal_connect( G_OBJECT( conv->ii ), "destroy", G_CALLBACK( paintboxview_ii_destroy_cb ), pbv ); paintboxview_ii_undo_changed_cb( conv->ii, pbv ); } /* Update ink display for the new image. */ conversion_set_image( IMAGEDISPLAY( ink )->conv, imagemodel->ink ); } widget_visible( GTK_WIDGET( pbv ), imagemodel->show_paintbox ); if( !imagemodel->show_paintbox ) return; for( i = 0; i < IMAGEMODEL_LAST; i++ ) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( pbv->tool[i] ), i == (int) imagemodel->state ); fontbutton_set_font_name( FONTBUTTON( pbv->font ), pbv->imagemodel->font_name ); } static void paintboxview_link( Paintboxview *pbv, Imagemodel *imagemodel ) { #ifdef DEBUG printf( "paintboxview_link: %p\n", pbv ); #endif /*DEBUG*/ pbv->imagemodel = imagemodel; g_signal_connect( G_OBJECT( imagemodel ), "changed", G_CALLBACK( paintboxview_changed_cb ), pbv ); } Paintboxview * paintboxview_new( Imagemodel *imagemodel ) { Paintboxview *pbv = gtk_type_new( TYPE_PAINTBOXVIEW ); paintboxview_link( pbv, imagemodel ); return( pbv ); } ================================================ FILE: src/paintboxview.h ================================================ /* Decls for paintboxview.c ... widgets in the paint bar */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ extern iWindowShape paintboxview_shape[]; #define TYPE_PAINTBOXVIEW (paintboxview_get_type()) #define PAINTBOXVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PAINTBOXVIEW, Paintboxview )) #define PAINTBOXVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PAINTBOXVIEW, PaintboxviewClass )) #define IS_PAINTBOXVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PAINTBOXVIEW )) #define IS_PAINTBOXVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PAINTBOXVIEW )) struct _Paintboxview { GtkFrame parent_class; Imagemodel *imagemodel; /* Spot undo/redo changes on imagemodel->conv->ii. */ Imageinfo *ii; guint ii_undo_changed_sid; guint ii_destroy_sid; GtkWidget *undo; GtkWidget *redo; GtkWidget *clear; GtkWidget *tool[IMAGEMODEL_LAST]; Tslider *nib; GtkWidget *ink; GtkWidget *font; GtkWidget *text; }; typedef struct _PaintboxviewClass { GtkFrameClass parent_class; /* My methods. */ } PaintboxviewClass; GtkType paintboxview_get_type( void ); Paintboxview *paintboxview_new( Imagemodel *imagemodel ); ================================================ FILE: src/pane.c ================================================ /* a side panel that can slide in and out of view */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* Our signals. */ enum { SIG_CHANGED, /* Change to position or openness */ SIG_LAST }; static GtkHPanedClass *parent_class = NULL; static guint pane_signals[SIG_LAST] = { 0 }; #ifdef DEBUG static char * pane_handedness2char( PaneHandedness handedness ) { switch( handedness ) { case PANE_HIDE_LEFT: return( "PANE_HIDE_LEFT" ); case PANE_HIDE_RIGHT: return( "PANE_HIDE_RIGHT" ); default: g_assert( 0 ); } } #endif /*DEBUG*/ static void pane_changed( Pane *pane ) { g_assert( IS_PANE( pane ) ); #ifdef DEBUG printf( "pane_changed: %p %s\n", pane, pane_handedness2char( pane->handedness ) ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( pane ), pane_signals[SIG_CHANGED], 0 ); } static int pane_closed_position( Pane *pane ) { /* Can't use max/min since we need to be able to work before our * window has been built. */ return( pane->handedness == PANE_HIDE_RIGHT ? 10000 : 0 ); } /* An open position ... used in case we are asked to open, but the position is * already closed. */ static int pane_open_position( Pane *pane ) { int max_position; int min_position; g_object_get( pane, "max_position", &max_position, "min_position", &min_position, NULL ); return( pane->handedness == PANE_HIDE_RIGHT ? max_position - 200: min_position + 200 ); } static void pane_destroy( GtkObject *object ) { Pane *pane; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PANE( object ) ); pane = PANE( object ); #ifdef DEBUG printf( "pane_destroy: %p %s\n", pane, pane_handedness2char( pane->handedness ) ); #endif /*DEBUG*/ /* My instance destroy stuff. */ IM_FREEF( g_source_remove, pane->animate_timeout ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void pane_class_init( PaneClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = pane_destroy; class->changed = NULL; pane_signals[SIG_CHANGED] = g_signal_new( "changed", G_OBJECT_CLASS_TYPE( object_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( PaneClass, changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } /* Position property has changed. We block the notify signal before we set * position, so this change must have come from a user drag or parent window * resize. */ static void pane_notify_position_cb( Pane *pane ) { int max_position; int min_position; int position; /* Can get here even though we block notify during position set in * animate, because of delays in window setup. */ if( pane->animate_timeout ) return; g_object_get( pane, "max_position", &max_position, "min_position", &min_position, NULL ); /* We can have 10,000 as position (meaning way to the * right), take account of any clipping there may be. */ pane->position = IM_CLIP( min_position, pane->position, max_position ); /* And the new value. */ position = gtk_paned_get_position( GTK_PANED( pane ) ); #ifdef DEBUG printf( "pane_notify_position_cb: %p %s %d\n", pane, pane_handedness2char( pane->handedness ), position ); #endif /*DEBUG*/ pane_set_position( pane, position ); pane_set_user_position( pane, position ); /* Look for dragged close. */ if( pane->open && pane->handedness == PANE_HIDE_LEFT && position == min_position ) pane_set_open( pane, FALSE ); if( pane->open && pane->handedness == PANE_HIDE_RIGHT && position == max_position ) pane_set_open( pane, FALSE ); } static void pane_init( Pane *pane ) { pane->handedness = PANE_HIDE_LEFT; pane->panechild = NULL; pane->open = FALSE; pane->position = 0; pane->user_position = 0; /* overwritten on _link() */ pane->target_position = 0; pane->close_on_end = FALSE; pane->last_set_position = 0; pane->animate_timeout = 0; g_signal_connect( pane, "notify::position", G_CALLBACK( pane_notify_position_cb ), NULL ); } GType pane_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PaneClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) pane_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Pane ), 32, /* n_preallocs */ (GInstanceInitFunc) pane_init, }; type = g_type_register_static( GTK_TYPE_HPANED, "Pane", &info, 0 ); } return( type ); } /* Operations on the model. */ void pane_set_position( Pane *pane, int position ) { if( pane->position != position ) { #ifdef DEBUG printf( "pane_set_position: %p %s %d\n", pane, pane_handedness2char( pane->handedness ), position ); #endif /*DEBUG*/ g_signal_handlers_block_by_func( pane, pane_notify_position_cb, NULL ); gtk_paned_set_position( GTK_PANED( pane ), position ); g_signal_handlers_unblock_by_func( pane, pane_notify_position_cb, NULL ); pane->position = position; pane_changed( pane ); } } void pane_set_user_position( Pane *pane, int user_position ) { if( pane->user_position != user_position ) { #ifdef DEBUG printf( "pane_set_user_position: %p %s %d\n", pane, pane_handedness2char( pane->handedness ), user_position ); #endif /*DEBUG*/ pane->user_position = user_position; pane_changed( pane ); } } void pane_set_open( Pane *pane, gboolean open ) { if( pane->open != open ) { #ifdef DEBUG printf( "pane_set_open: %p %s %d\n", pane, pane_handedness2char( pane->handedness ), open ); #endif /*DEBUG*/ widget_visible( GTK_WIDGET( pane->panechild ), open ); pane->open = open; pane_changed( pane ); } } /* Set everything all at once on startup. */ void pane_set_state( Pane *pane, gboolean open, int user_position ) { if( pane->open != open || pane->user_position != user_position ) { g_signal_handlers_block_by_func( pane, pane_notify_position_cb, NULL ); gtk_paned_set_position( GTK_PANED( pane ), user_position ); g_signal_handlers_unblock_by_func( pane, pane_notify_position_cb, NULL ); widget_visible( GTK_WIDGET( pane->panechild ), open ); pane->open = open; pane->user_position = user_position; pane->position = user_position; pane_changed( pane ); } } void pane_set_child( Pane *pane, Panechild *panechild ) { g_assert( !pane->panechild ); pane->panechild = panechild; if( pane->handedness == PANE_HIDE_LEFT ) gtk_paned_pack1( GTK_PANED( pane ), GTK_WIDGET( panechild ), TRUE, TRUE ); else gtk_paned_pack2( GTK_PANED( pane ), GTK_WIDGET( panechild ), TRUE, TRUE ); } /* Control. */ static gboolean pane_animate_timeout_cb( Pane *pane ) { int position = pane->position; int target = pane->target_position; int new; gboolean more; #ifdef DEBUG printf( "pane_animate_timeout_cb: %p %s\n", pane, pane_handedness2char( pane->handedness ) ); #endif /*DEBUG*/ more = TRUE; new = position + (target - position) / 2; if( ABS( position - target ) < 5 || new == pane->last_set_position ) { /* At our target! */ new = target; more = FALSE; pane->animate_timeout = 0; } pane_set_position( pane, new ); pane->last_set_position = new; if( !more && pane->close_on_end ) pane_set_open( pane, FALSE ); return( more ); } /* Close the pane with an animation. */ void pane_animate_closed( Pane *pane ) { if( !pane->animate_timeout && pane->open ) { int max_position; int min_position; int target_position; target_position = pane_closed_position( pane ); g_object_get( pane, "max_position", &max_position, "min_position", &min_position, NULL ); /* Can be zero if we're here very early. */ if( max_position > 0 ) target_position = IM_CLIP( min_position, target_position, max_position ); pane->target_position = target_position; pane->close_on_end = TRUE; pane->last_set_position = -1; pane->animate_timeout = g_timeout_add( 50, (GSourceFunc) pane_animate_timeout_cb, pane ); } } /* Open the pane with an animation. */ void pane_animate_open( Pane *pane ) { if( !pane->animate_timeout && !pane->open ) { int max_position; int min_position; int target_position; target_position = pane->user_position; g_object_get( pane, "max_position", &max_position, "min_position", &min_position, NULL ); /* Can be zero if we're here very early. */ if( max_position > 0 ) target_position = IM_CLIP( min_position, target_position, max_position ); /* user_position can be max or min if the pane was dragged * closed. */ if( target_position == max_position || target_position == min_position ) target_position = pane_open_position( pane ); #ifdef DEBUG printf( "pane_animate_open: %p %s %d\n", pane, pane_handedness2char( pane->handedness ), target_position ); #endif /*DEBUG*/ pane->target_position = target_position; pane->close_on_end = FALSE; pane->last_set_position = -1; pane_set_open( pane, TRUE ); pane->animate_timeout = g_timeout_add( 50, (GSourceFunc) pane_animate_timeout_cb, pane ); } } static void pane_link( Pane *pane, PaneHandedness handedness ) { #ifdef DEBUG printf( "pane_link: %p %s\n", pane, pane_handedness2char( handedness ) ); #endif /*DEBUG*/ pane->handedness = handedness; pane_set_open( pane, FALSE ); } Pane * pane_new( PaneHandedness handedness ) { Pane *pane; pane = PANE( g_object_new( TYPE_PANE, NULL ) ); pane_link( pane, handedness ); return( pane ); } ================================================ FILE: src/pane.h ================================================ /* a side panel that can slide in and out of view */ /* Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PANE (pane_get_type()) #define PANE( obj ) (GTK_CHECK_CAST( (obj), TYPE_PANE, Pane )) #define PANE_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PANE, PaneClass )) #define IS_PANE( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PANE )) #define IS_PANE_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PANE )) /* Can hide on the left or the right hand side of a window. */ typedef enum { PANE_HIDE_LEFT, PANE_HIDE_RIGHT } PaneHandedness; typedef struct _Pane { GtkHPaned parent_object; PaneHandedness handedness; /* Hide on left or right */ /* The child pane we show on left or right. */ Panechild *panechild; /* Are we visible or not. */ gboolean open; /* The position of the divider. This changes as the pane is animated * open and closed and does not reflect the position the user has * selected by dragging. */ int position; /* The position the user wants the pane to sit at. */ int user_position; /* Animating towards this position. If close_on_end is true, close the * pane at the end of animation. */ int target_position; gboolean close_on_end; /* Set animation speed with this. */ int last_set_position; /* Timeout for animation. */ guint animate_timeout; } Pane; typedef struct _PaneClass { GtkHPanedClass parent_class; /* Either position or open have changed. */ void (*changed)( Pane * ); } PaneClass; GType pane_get_type( void ); Pane *pane_new( PaneHandedness handedness ); void pane_set_position( Pane *pane, int position ); void pane_set_user_position( Pane *pane, int user_position ); void pane_set_open( Pane *pane, gboolean open ); void pane_set_state( Pane *pane, gboolean open, int user_position ); void pane_set_child( Pane *pane, Panechild *panechild ); void pane_animate_closed( Pane *pane ); void pane_animate_open( Pane *pane ); ================================================ FILE: src/panechild.c ================================================ /* The thing that sits in a pane showing the title and close button. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static void panechild_finalize( GObject *gobject ) { Panechild *panechild = PANECHILD( gobject ); #ifdef DEBUG printf( "panechild_finalize\n" ); #endif /*DEBUG*/ /* My instance finalize stuff. */ IM_FREE( panechild->title ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void panechild_refresh( vObject *vobject ) { Panechild *panechild = PANECHILD( vobject ); #ifdef DEBUG printf( "panechild_refresh:\n" ); #endif /*DEBUG*/ set_glabel( panechild->label, "%s", panechild->title ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void panechild_class_init( PanechildClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = panechild_finalize; vobject_class->refresh = panechild_refresh; } static void panechild_hide_cb( GtkWidget *wid, Panechild *panechild ) { pane_animate_closed( panechild->pane ); } static void panechild_init( Panechild *panechild ) { GtkWidget *hbox; GtkWidget *but; GtkWidget *icon; #ifdef DEBUG printf( "panechild_init:\n" ); #endif /*DEBUG*/ panechild->pane = NULL; panechild->title = NULL; panechild->label = NULL; hbox = gtk_hbox_new( FALSE, 7 ); gtk_box_pack_start( GTK_BOX( panechild ), hbox, FALSE, FALSE, 0 ); but = gtk_button_new(); gtk_button_set_relief( GTK_BUTTON( but ), GTK_RELIEF_NONE ); gtk_box_pack_end( GTK_BOX( hbox ), but, FALSE, FALSE, 0 ); set_tooltip( but, _( "Close the pane" ) ); icon = gtk_image_new_from_stock( GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU ); gtk_container_add( GTK_CONTAINER( but ), icon ); gtk_signal_connect( GTK_OBJECT( but ), "clicked", GTK_SIGNAL_FUNC( panechild_hide_cb ), panechild ); panechild->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( panechild->label ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( hbox ), panechild->label, TRUE, TRUE, 2 ); gtk_widget_show_all( hbox ); } GtkType panechild_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PanechildClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) panechild_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Panechild ), 32, /* n_preallocs */ (GInstanceInitFunc) panechild_init, }; type = g_type_register_static( TYPE_VOBJECT, "Panechild", &info, 0 ); } return( type ); } Panechild * panechild_new( Pane *pane, const char *title ) { Panechild *panechild = gtk_type_new( TYPE_PANECHILD ); IM_SETSTR( panechild->title, title ); panechild->pane = pane; pane_set_child( pane, panechild ); return( panechild ); } ================================================ FILE: src/panechild.h ================================================ /* The thing that sits in a pane showing the title and close button. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PANECHILD (panechild_get_type()) #define PANECHILD( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PANECHILD, Panechild )) #define PANECHILD_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PANECHILD, PanechildClass )) #define IS_PANECHILD( obj ) \ (GTK_CHECK_TYPE( (obj), TYPE_PANECHILD )) #define IS_PANECHILD_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PANECHILD )) struct _Panechild { vObject parent_object; Pane *pane; /* The pane we are part of */ const char *title; /* Title we display */ GtkWidget *label; /* Titlebar label */ }; typedef struct _PanechildClass { vObjectClass parent_class; } PanechildClass; GtkType panechild_get_type( void ); Panechild *panechild_new( Pane *pane, const char *title ); ================================================ FILE: src/parse.y ================================================ %{ /* Parse ip's macro language. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* trace text read system #define DEBUG_CHARACTER */ /* The lexer from lex.l. */ int yylex( void ); void yyrestart( FILE *input_file ); /* Declare file-private stuff, shared with the lexer. Bison will put this * stuff into parse.h, so just declare, don't define. Sadly we can't have * these things static :( */ /* Global .. the symbol whose definition we are currently parsing, the symbol * which all defs in this parse action should be made local to. */ extern Symbol *current_symbol; extern Symbol *root_symbol; /* The current parse context. */ extern Compile *current_compile; extern ParseNode *current_parsenode; /* The kit we are adding new symbols to. */ extern Toolkit *current_kit; /* Where it should go in the kit. */ extern int tool_position; /* Lineno of start of last top-level def. */ extern int last_top_lineno; /* Text we've gathered in this lex. */ extern char lex_text_buffer[MAX_STRSIZE]; /* Stack of symbols for parser - each represents a new scope level. */ extern Symbol *scope_stack_symbol[MAX_SSTACK]; extern Compile *scope_stack_compile[MAX_SSTACK]; extern int scope_sp; /* Use to generate unique ids for anonymouse parse objects (eg. lambdas etc). */ extern int parse_object_id; /* Get text for parsed objects. */ char *input_text( char *out ); void input_reset( void ); void input_push( int n ); void input_backtoch( char ch ); void input_back1( void ); void input_pop( void ); /* Nest and unnest scopes. */ void scope_push( void ); void scope_pop( void ); void scope_pop_all( void ); void scope_reset( void ); /* Helper functions. */ void *parse_toplevel_end( Symbol *sym ); void *parse_access_end( Symbol *sym, Symbol *main ); %} %union { struct sym_table *yy_symtab; ParseNode *yy_node; char *yy_name; ParseConst yy_const; UnOp yy_uop; BinOp yy_binop; } %token TK_TAG TK_IDENT TK_CONST TK_DOTDOTDOT TK_LAMBDA TK_FROM TK_TO TK_SUCHTHAT %token TK_UMINUS TK_UPLUS TK_POW %token TK_LESS TK_LESSEQ TK_MORE TK_MOREEQ TK_NOTEQ %token TK_LAND TK_LOR TK_BAND TK_BOR TK_JOIN TK_DIFF %token TK_IF TK_THEN TK_ELSE %token TK_CHAR TK_SHORT TK_CLASS TK_SCOPE %token TK_INT TK_FLOAT TK_DOUBLE TK_SIGNED TK_UNSIGNED TK_COMPLEX %token TK_SEPARATOR TK_DIALOG TK_LSHIFT TK_RSHIFT %type expr binop uop rhs list_expression comma_list body %type simple_pattern complex_pattern list_pattern %type leaf_pattern %type crhs cexprlist prhs lambda %type TK_CONST %type TK_IDENT TK_TAG %left TK_SUCHTHAT %left TK_LAMBDA %nonassoc TK_IF %left ',' %left TK_TO %left TK_LOR %left TK_LAND '@' %left TK_BOR %left '^' %left TK_BAND %nonassoc TK_EQ TK_NOTEQ TK_PEQ TK_PNOTEQ %nonassoc TK_LESS TK_LESSEQ TK_MORE TK_MOREEQ %left TK_LSHIFT TK_RSHIFT %left '+' '-' %left '*' '/' '%' %left '!' '~' TK_JOIN TK_DIFF TK_UMINUS TK_UPLUS %right TK_POW ':' %right TK_CONST '(' %right TK_IDENT TK_TAG TK_SCOPE '[' %right TK_APPLICATION %left '?' '.' %start select /* Our syntax for list comprehensions is not LALR(1). We have: simple_pattern '<-' expr ';' | expr ';' simple_pattern can be something like a:x which is also an expr. We don't know which branch to take until we see a '<' or a ';'. Use bison's GLR system to parse this, and ignore the first 13 reduce/reduce conflicts caused by this ambiguity. FIXME ... we now depend on bison, but we still have some yacc compatibility stuff in here, and we don't use all of bison's nice features (eg. for tracking line numbers in the source file). Fix this up at some stage. */ %glr-parser %expect-rr 13 %error-verbose %% select: ',' main | '^' single_definition | '*' params_plus_rhs optsemi { compile_check( current_compile ); } | prhs { char buf[MAX_STRSIZE]; current_compile->tree = $1; /* Junk any old text. */ IM_FREE( current_compile->text ); IM_FREE( current_compile->prhstext ); IM_FREE( current_compile->rhstext ); /* Set new text. */ IM_SETSTR( current_compile->rhstext, input_text( buf ) ); compile_check( current_compile ); } ; prhs: TK_BAND expr { $$ = $2; } | '@' cexprlist { $$ = $2; } ; main: /* Empty */ | main single_definition ; single_definition: directive { tool_position += 1; } | toplevel_definition optsemi { tool_position += 1; } ; directive: TK_SEPARATOR { Tool *tool; if( !is_top( current_symbol ) ) yyerror( _( "not top level" ) ); tool = tool_new_sep( current_kit, tool_position ); tool->lineno = input_state.lineno; input_reset(); } | TK_DIALOG TK_CONST TK_CONST { Tool *tool; if( !is_top( current_symbol ) ) yyerror( _( "not top level" ) ); /* Should have two strings. */ if( $2.type != PARSE_CONST_STR || $3.type != PARSE_CONST_STR ) yyerror( _( "not strings" ) ); /* Add tool. */ tool = tool_new_dia( current_kit, tool_position, $2.val.str, $3.val.str ); if( !tool ) yyerror( error_get_sub() ); tool->lineno = input_state.lineno; /* Cast away const here. */ tree_const_destroy( (ParseConst *) &$2 ); tree_const_destroy( (ParseConst *) &$3 ); input_reset(); } ; toplevel_definition: { last_top_lineno = input_state.lineno; scope_reset(); current_compile = root_symbol->expr->compile; } definition { input_reset(); } ; /* Parse a new defining occurence. This can be a local or a top-level. */ definition: simple_pattern { Symbol *sym; /* Two forms: , or . * Enforce the no-args-to-pattern-assignment rule in the arg * pattern parser. */ if( $1->type == NODE_LEAF ) { const char *name = IOBJECT( $1->leaf )->name; /* Make a new defining occurence. */ sym = symbol_new_defining( current_compile, name ); (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); } else { char name[256]; /* We have . Make an anon symbol for this * value, then the variables in the pattern become * toplevels which access that. */ if( !compile_pattern_has_leaf( $1 ) ) yyerror( _( "left-hand-side pattern " "contains no identifiers" ) ); im_snprintf( name, 256, "$$pattern_lhs%d", parse_object_id++ ); sym = symbol_new_defining( current_compile, name ); sym->generated = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); } /* Note on the enclosing last_sym. Things like the program * window use this to work out what sym to display after a * parse. symbol_dispose() is careful to NULL this out. */ current_compile->last_sym = sym; /* Initialise symbol parsing variables. Save old current symbol, * add new one. */ scope_push(); current_symbol = sym; current_compile = sym->expr->compile; g_assert( !current_compile->param ); g_assert( current_compile->nparam == 0 ); /* Junk any old def text. */ IM_FREE( current_compile->text ); IM_FREE( current_compile->prhstext ); IM_FREE( current_compile->rhstext ); } params_plus_rhs { compile_check( current_compile ); /* Link unresolved names into the outer scope. */ compile_resolve_names( current_compile, compile_get_parent( current_compile ) ); /* Is this the end of a top-level? Needs extra work to add to * the enclosing toolkit etc. */ if( is_scope( symbol_get_parent( current_symbol ) ) ) parse_toplevel_end( current_symbol ); /* Is this a pattern definition? Expand the pattern to a * set of access defs. */ if( $1->type != NODE_LEAF ) { Compile *parent = compile_get_parent( current_compile ); GSList *built_syms; built_syms = compile_pattern_lhs( parent, current_symbol, $1 ); if( is_scope( symbol_get_parent( current_symbol ) ) ) slist_map( built_syms, (SListMapFn) parse_toplevel_end, NULL ); slist_map( built_syms, (SListMapFn) parse_access_end, current_symbol ); g_slist_free( built_syms ); } scope_pop(); } ; /* Parse params/body/locals into current_expr */ params_plus_rhs: { input_push( 1 ); /* We've already read the character past the end of the * identifier (that's why we know the identifier is over). */ input_back1(); } params { input_push( 2 ); input_backtoch( '=' ); } body { current_compile->tree = $4; g_assert( current_compile->tree ); input_push( 4 ); } locals { char buf[MAX_STRSIZE]; input_pop(); /* Save body text as rhstext. */ IM_SETSTR( current_compile->rhstext, input_text( buf ) ); input_pop(); /* Save params '=' body as prhstext. */ IM_SETSTR( current_compile->prhstext, input_text( buf ) ); input_pop(); /* Save full text of definition. */ IM_SETSTR( current_compile->text, input_text( buf ) ); #ifdef DEBUG printf( "%s->compile->text = \"%s\"\n", IOBJECT( current_compile->sym )->name, current_compile->text ); printf( "%s->compile->prhstext = \"%s\"\n", IOBJECT( current_compile->sym )->name, current_compile->prhstext ); printf( "%s->compile->rhstext = \"%s\"\n", IOBJECT( current_compile->sym )->name, current_compile->rhstext ); #endif /*DEBUG*/ } ; params: /* Empty */ | params simple_pattern { Symbol *sym; /* If the pattern is just an identifier, make it a direct * parameter. Otherwise make an anon param and put the pattern * in as a local with the same id. * * fred [a] = 12; * * parses to: * * fred $$arg42 = 12 { $$patt42 = [a]; } * * A later pass creates the "a = $$arg42?0" definition. */ if( $2->type == NODE_LEAF ) { const char *name = IOBJECT( $2->leaf )->name; /* Make defining occurence. */ sym = symbol_new_defining( current_compile, name ); (void) symbol_parameter_init( sym ); } else { char name[256]; im_snprintf( name, 256, "$$arg%d", parse_object_id ); sym = symbol_new_defining( current_compile, name ); sym->generated = TRUE; (void) symbol_parameter_init( sym ); im_snprintf( name, 256, "$$patt%d", parse_object_id++ ); sym = symbol_new_defining( current_compile, name ); sym->generated = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); sym->expr->compile->tree = $2; } } ; body : '=' TK_CLASS crhs { $$ = $3; } | rhs { $$ = $1; } ; crhs: { ParseNode *pn = tree_class_new( current_compile ); input_push( 3 ); scope_push(); current_symbol = current_compile->super; current_compile = current_symbol->expr->compile; current_parsenode = pn; } cexprlist { Compile *parent = compile_get_parent( current_compile ); char buf[MAX_STRSIZE]; int len; (void) input_text( buf ); /* Always read 1 char too many. */ if( (len = strlen( buf )) > 0 ) buf[len - 1] = '\0'; IM_SETSTR( current_compile->rhstext, buf ); input_pop(); current_compile->tree = $2; if( current_compile->tree->elist ) parent->has_super = TRUE; /* Do some checking. */ compile_check( current_compile ); /* Link unresolved names. */ compile_resolve_names( current_compile, parent ); scope_pop(); $$ = current_parsenode; current_parsenode = NULL; } ; rhs: '=' expr { $$ = $2; } | '=' expr ',' expr optsemi rhs { $$ = tree_ifelse_new( current_compile, $4, $2, $6 ); } ; locals: ';' | '{' deflist '}' | '{' '}' ; optsemi: /* Empty */ | ';' optsemi ; deflist: definition { input_pop(); input_push( 5 ); } optsemi | deflist definition { input_pop(); input_push( 6 ); } optsemi ; cexprlist: /* Empty */ { $$ = tree_super_new( current_compile ); } | cexprlist expr %prec TK_APPLICATION { $$ = tree_super_extend( current_compile, $1, $2 ); } ; expr: '(' expr ')' { $$ = $2; } | TK_CONST { $$ = tree_const_new( current_compile, $1 ); } | TK_IDENT { $$ = tree_leaf_new( current_compile, $1 ); im_free( $1 ); } | TK_TAG { $$ = tree_tag_new( current_compile, $1 ); im_free( $1 ); } | TK_SCOPE { $$ = tree_leaf_new( current_compile, IOBJECT( symbol_get_scope( current_symbol ) )->name ); } | TK_IF expr TK_THEN expr TK_ELSE expr %prec TK_IF { $$ = tree_ifelse_new( current_compile, $2, $4, $6 ); } | expr expr %prec TK_APPLICATION { $$ = tree_appl_new( current_compile, $1, $2 ); } | lambda | list_expression { $$ = $1; } | '(' expr ',' expr ')' { $$ = tree_binop_new( current_compile, BI_COMMA, $2, $4 ); } | binop | uop ; lambda: TK_LAMBDA TK_IDENT %prec TK_LAMBDA { char name[256]; Symbol *sym; /* Make an anonymous symbol local to the current sym, compile * the expr inside that. */ im_snprintf( name, 256, "$$lambda%d", parse_object_id++ ); sym = symbol_new_defining( current_compile, name ); sym->generated = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); /* Initialise symbol parsing variables. Save old current symbol, * add new one. */ scope_push(); current_symbol = sym; current_compile = sym->expr->compile; /* Make the parameter. */ sym = symbol_new_defining( current_compile, $2 ); symbol_parameter_init( sym ); im_free( $2 ); } expr { Symbol *sym; current_compile->tree = $4; if( !compile_check( current_compile ) ) yyerror( error_get_sub() ); /* Link unresolved names in to the outer scope. */ compile_resolve_names( current_compile, compile_get_parent( current_compile ) ); /* The value of the expr is the anon we defined. */ sym = current_symbol; scope_pop(); $$ = tree_leafsym_new( current_compile, sym ); } ; list_expression: '[' expr TK_DOTDOTDOT ']' { $$ = tree_generator_new( current_compile, $2, NULL, NULL ); } | '[' expr TK_DOTDOTDOT expr ']' { $$ = tree_generator_new( current_compile, $2, NULL, $4 ); } | '[' expr ',' expr TK_DOTDOTDOT ']' { $$ = tree_generator_new( current_compile, $2, $4, NULL ); } | '[' expr ',' expr TK_DOTDOTDOT expr ']' { $$ = tree_generator_new( current_compile, $2, $4, $6 ); } | '[' expr TK_SUCHTHAT { char name[256]; Symbol *sym; Compile *enclosing = current_compile; /* Make an anonymous symbol local to the current sym, copy * the map expr inside that. */ im_snprintf( name, 256, "$$lcomp%d", parse_object_id++ ); sym = symbol_new_defining( current_compile, name ); (void) symbol_user_init( sym ); sym->generated = TRUE; (void) compile_new_local( sym->expr ); /* Push a new scope. */ scope_push(); current_symbol = sym; current_compile = sym->expr->compile; /* Somewhere to save the result expr. We have to copy the * expr, as we want it to be bound in $$lcomp's context so * that it can see the generators. */ sym = symbol_new_defining( current_compile, "$$result" ); sym->generated = TRUE; sym->placeholder = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); sym->expr->compile->tree = compile_copy_tree( enclosing, $2, sym->expr->compile ); } generator frompred_list ']' { Symbol *sym; /* The map expr can refer to generator names. Resolve inwards * so it links to the generators. */ compile_resolve_names( compile_get_parent( current_compile ), current_compile ); /* Generate the code for the list comp. */ compile_lcomp( current_compile ); compile_check( current_compile ); /* Link unresolved names outwards. */ compile_resolve_names( current_compile, compile_get_parent( current_compile ) ); /* The value of the expr is the anon we defined. */ sym = current_symbol; scope_pop(); $$ = tree_leafsym_new( current_compile, sym ); } | '[' comma_list ']' { $$ = $2; } | '[' ']' { ParseConst elist; elist.type = PARSE_CONST_ELIST; $$ = tree_const_new( current_compile, elist ); } ; frompred_list: /* Empty */ { } | frompred_list ';' frompred { } ; generator: simple_pattern TK_FROM expr { char name[256]; Symbol *sym; im_snprintf( name, 256, "$$pattern%d", parse_object_id ); sym = symbol_new_defining( current_compile, name ); sym->generated = TRUE; sym->placeholder = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); sym->expr->compile->tree = $1; im_snprintf( name, 256, "$$generator%d", parse_object_id++ ); sym = symbol_new_defining( current_compile, name ); sym->generated = TRUE; sym->placeholder = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); sym->expr->compile->tree = $3; } ; frompred: generator | expr { char name[256]; Symbol *sym; im_snprintf( name, 256, "$$filter%d", parse_object_id++ ); sym = symbol_new_defining( current_compile, name ); sym->generated = TRUE; sym->placeholder = TRUE; (void) symbol_user_init( sym ); (void) compile_new_local( sym->expr ); sym->expr->compile->tree = $1; } ; comma_list: expr ',' comma_list { $$ = tree_lconst_extend( current_compile, $3, $1 ); } | expr { $$ = tree_lconst_new( current_compile, $1 ); } ; /* How odd, break the "'+' { BI_ADD } | ..." into a separate production and we * get reduce/reduce conflits. Copypaste a lot instead. */ binop: expr '+' expr { $$ = tree_binop_new( current_compile, BI_ADD, $1, $3 ); } | expr ':' expr { $$ = tree_binop_new( current_compile, BI_CONS, $1, $3 ); } | expr '-' expr { $$ = tree_binop_new( current_compile, BI_SUB, $1, $3 ); } | expr '?' expr { $$ = tree_binop_new( current_compile, BI_SELECT, $1, $3 ); } | expr '/' expr { $$ = tree_binop_new( current_compile, BI_DIV, $1, $3 ); } | expr '*' expr { $$ = tree_binop_new( current_compile, BI_MUL, $1, $3 ); } | expr '%' expr { $$ = tree_binop_new( current_compile, BI_REM, $1, $3 ); } | expr TK_JOIN expr { $$ = tree_binop_new( current_compile, BI_JOIN, $1, $3 ); } | expr TK_POW expr { $$ = tree_binop_new( current_compile, BI_POW, $1, $3 ); } | expr TK_LSHIFT expr { $$ = tree_binop_new( current_compile, BI_LSHIFT, $1, $3 ); } | expr TK_RSHIFT expr { $$ = tree_binop_new( current_compile, BI_RSHIFT, $1, $3 ); } | expr '^' expr { $$ = tree_binop_new( current_compile, BI_EOR, $1, $3 ); } | expr TK_LAND expr { $$ = tree_binop_new( current_compile, BI_LAND, $1, $3 ); } | expr TK_BAND expr { $$ = tree_binop_new( current_compile, BI_BAND, $1, $3 ); } | expr '@' expr { $$ = tree_compose_new( current_compile, $1, $3 ); } | expr TK_LOR expr { $$ = tree_binop_new( current_compile, BI_LOR, $1, $3 ); } | expr TK_BOR expr { $$ = tree_binop_new( current_compile, BI_BOR, $1, $3 ); } | expr TK_LESS expr { $$ = tree_binop_new( current_compile, BI_LESS, $1, $3 ); } | expr TK_LESSEQ expr { $$ = tree_binop_new( current_compile, BI_LESSEQ, $1, $3 ); } | expr TK_MORE expr { $$ = tree_binop_new( current_compile, BI_MORE, $1, $3 ); } | expr TK_MOREEQ expr { $$ = tree_binop_new( current_compile, BI_MOREEQ, $1, $3 ); } | expr TK_EQ expr { $$ = tree_binop_new( current_compile, BI_EQ, $1, $3 ); } | expr TK_NOTEQ expr { $$ = tree_binop_new( current_compile, BI_NOTEQ, $1, $3 ); } | expr TK_PEQ expr { $$ = tree_binop_new( current_compile, BI_PEQ, $1, $3 ); } | expr TK_PNOTEQ expr { $$ = tree_binop_new( current_compile, BI_PNOTEQ, $1, $3 ); } | expr '.' expr { $$ = tree_binop_new( current_compile, BI_DOT, $1, $3 ); } | expr TK_DIFF expr { ParseNode *pn1, *pn2; pn1 = tree_leaf_new( current_compile, "difference" ); pn2 = tree_leaf_new( current_compile, "equal" ); pn1 = tree_appl_new( current_compile, pn1, pn2 ); pn1 = tree_appl_new( current_compile, pn1, $1 ); pn1 = tree_appl_new( current_compile, pn1, $3 ); $$ = pn1; } | expr TK_TO expr { ParseNode *pn; pn = tree_leaf_new( current_compile, "mknvpair" ); pn = tree_appl_new( current_compile, pn, $1 ); pn = tree_appl_new( current_compile, pn, $3 ); $$ = pn; } ; signed: /* Nothing */ | TK_SIGNED ; unsigned: /* Nothing */ | TK_UNSIGNED ; uop: '(' unsigned TK_CHAR ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CUCHAR, $5 ); } | '(' TK_SIGNED TK_CHAR ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CSCHAR, $5 ); } | '(' signed TK_SHORT ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CSSHORT, $5 ); } | '(' TK_UNSIGNED TK_SHORT ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CUSHORT, $5 ); } | '(' signed TK_INT ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CSINT, $5 ); } | '(' TK_UNSIGNED TK_INT ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CUINT, $5 ); } | '(' TK_FLOAT ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CFLOAT, $4 ); } | '(' TK_DOUBLE ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CDOUBLE, $4 ); } | '(' TK_COMPLEX ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CCOMPLEX, $4 ); } | '(' TK_DOUBLE TK_COMPLEX ')' expr %prec TK_UMINUS { $$ = tree_unop_new( current_compile, UN_CDCOMPLEX, $5 ); } | TK_UMINUS expr { $$ = tree_unop_new( current_compile, UN_MINUS, $2 ); } | '!' expr { $$ = tree_unop_new( current_compile, UN_NEG, $2 ); } | '~' expr { $$ = tree_unop_new( current_compile, UN_COMPLEMENT, $2 ); } | TK_UPLUS expr { $$ = tree_unop_new( current_compile, UN_PLUS, $2 ); } ; /* Stuff that can appear on the LHS of an equals, or as a parameter pattern. */ simple_pattern: leaf_pattern | '(' leaf_pattern ',' leaf_pattern ')' { $$ = tree_binop_new( current_compile, BI_COMMA, $2, $4 ); } | simple_pattern ':' simple_pattern { $$ = tree_binop_new( current_compile, BI_CONS, $1, $3 ); } | '(' complex_pattern ')' { $$ = $2; } | '[' list_pattern ']' { $$ = $2; } | '[' ']' { ParseConst elist; elist.type = PARSE_CONST_ELIST; $$ = tree_const_new( current_compile, elist ); } ; /* Stuff that can appear in a complex (a, b) pattern. */ leaf_pattern: TK_IDENT { $$ = tree_leaf_new( current_compile, $1 ); im_free( $1 ); } | TK_CONST { $$ = tree_const_new( current_compile, $1 ); } ; /* What can appear in round brackets or a comma list. */ complex_pattern: TK_IDENT TK_IDENT { $$ = tree_pattern_class_new( current_compile, $1, tree_leaf_new( current_compile, $2 ) ); im_free( $1 ); im_free( $2 ); } | simple_pattern ; list_pattern: complex_pattern ',' list_pattern { $$ = tree_lconst_extend( current_compile, $3, $1 ); } | complex_pattern { $$ = tree_lconst_new( current_compile, $1 ); } ; %% /* Return point on syntax error. */ jmp_buf parse_error_point; /* Text we've lexed. */ char lex_text_buffer[MAX_STRSIZE]; VipsBuf lex_text = VIPS_BUF_STATIC( lex_text_buffer ); /* State of input system. */ InputState input_state; /* Defintions for the static decls at the top. We have to put the defs down * here to mkake sure they don't creep in to the generated parser.h. */ /* Actually, we can't make these static :-( since they are declared extern at * the top of the file. */ Symbol *current_symbol; Symbol *root_symbol; Compile *current_compile = NULL; ParseNode *current_parsenode = NULL; Toolkit *current_kit; int tool_position; int last_top_lineno; Symbol *scope_stack_symbol[MAX_SSTACK]; Compile *scope_stack_compile[MAX_SSTACK]; int scope_sp = 0; int parse_object_id = 0; /* Here for errors in parse. * * Bison calls yyerror with only a char* arg. This printf() version is called * from nip2 in a few places during parse. */ void nip2yyerror( const char *sub, ... ) { va_list ap; char buf[4096]; va_start( ap, sub ); (void) im_vsnprintf( buf, 4096, sub, ap ); va_end( ap ); error_top( _( "Parse error." ) ); if( current_compile && current_compile->last_sym ) error_sub( _( "Error in %s: %s" ), IOBJECT( current_compile->last_sym )->name, buf ); else error_sub( _( "Error: %s" ), buf ); longjmp( parse_error_point, -1 ); } /* Bison calls this. */ void yyerror( const char *msg ) { nip2yyerror( "%s", msg ); } /* Attach yyinput to a file. */ void attach_input_file( iOpenFile *of ) { InputState *is = &input_state; #ifdef DEBUG printf( "attach_input_file: \"%s\"\n", of->fname ); #endif /*DEBUG*/ /* Need to clear flex/bison's buffers in case we abandoned the * previous parse. */ yyrestart( NULL ); is->of = of; is->str = NULL; is->strpos = NULL; is->bwp = 0; is->bspsp = 0; is->bsp[is->bspsp] = 0; is->lineno = 1; is->charno = 0; is->pcharno = 0; is->charpos = 0; is->oldchar = -1; /* Init text gatherer. */ vips_buf_rewind( &lex_text ); } /* Attach yyinput to a string. */ void attach_input_string( const char *str ) { InputState *is = &input_state; #ifdef DEBUG printf( "attach_input_string: \"%s\"\n", str ); #endif /*DEBUG*/ yyrestart( NULL ); is->of = NULL; is->str = (char *) str; is->strpos = (char *) str; is->bwp = 0; is->bspsp = 0; is->bsp[is->bspsp] = 0; is->lineno = 1; is->charno = 0; is->pcharno = 0; is->charpos = 0; is->oldchar = -1; /* Init text gatherer. */ vips_buf_rewind( &lex_text ); } /* Read a character from the input. */ int ip_input( void ) { InputState *is = &input_state; int ch; if( is->oldchar >= 0 ) { /* From unget buffer. */ ch = is->oldchar; is->oldchar = -1; } else if( is->of ) { /* Input from file. */ if( (ch = getc( is->of->fp )) == EOF ) return( 0 ); } else { /* Input from string. */ if( (ch = *is->strpos) ) is->strpos++; else /* No counts to update! */ return( 0 ); } /* Update counts. */ if( ch == '\n' ) { is->lineno++; is->pcharno = is->charno + 1; is->charno = 0; } is->charno++; is->charpos++; /* Add this character to the characters we have accumulated for this * definition. */ if( is->bwp >= MAX_STRSIZE ) yyerror( _( "definition is too long" ) ); if( is->bwp >= 0 ) is->buf[is->bwp] = ch; is->bwp++; /* Add to lex text buffer. */ if( is->charno > 0 ) vips_buf_appendc( &lex_text, ch ); #ifdef DEBUG_CHARACTER printf( "ip_input: returning '%c'\n", ch ); #endif /*DEBUG_CHARACTER*/ return( ch ); } /* Unget an input character. */ void ip_unput( int ch ) { InputState *is = &input_state; #ifdef DEBUG_CHARACTER printf( "ip_unput: ungetting '%c'\n", ch ); #endif /*DEBUG_CHARACTER*/ /* Is lex trying to unget the end-of-file marker? Do nothing if it is. */ if( !ch ) return; if( is->of ) { if( ungetc( ch, is->of->fp ) == EOF ) error( "unget buffer overflow" ); } else /* Save extra char here. */ is->oldchar = ch; /* Redo counts. */ if( ch == '\n' ) { is->lineno--; /* Restore previous charno. */ is->charno = is->pcharno; is->pcharno = 0; } is->charno--; is->charpos--; is->bwp--; /* Unget from lex text buffer. */ if( is->charno > 0 ) vips_buf_removec( &lex_text, ch ); } /* Test for end-of-input. */ gboolean is_EOF( void ) { InputState *is = &input_state; if( is->of ) return( feof( is->of->fp ) ); else return( *is->str == '\0' ); } /* Return the text we have accumulated for the current definition. Remove * leading and trailing whitespace and spare semicolons. out needs to be * MAX_STRSIZE. */ char * input_text( char *out ) { InputState *is = &input_state; const char *buf = is->buf; int start = is->bsp[is->bspsp]; int end = is->bwp; int len; int i; for( i = start; i < end && (isspace( buf[i] ) || buf[i] == ';'); i++ ) ; start = i; for( i = end - 1; i > start && (isspace( buf[i] ) || buf[i] == ';'); i-- ) ; end = i + 1; len = end - start; g_assert( len < MAX_STRSIZE - 1 ); im_strncpy( out, buf + start, len + 1 ); out[len] = '\0'; #ifdef DEBUG_CHARACTER printf( "input_text: level %d, returning \"%s\"\n", is->bspsp, out ); #endif /*DEBUG_CHARACTER*/ return( out ); } /* Reset/push/pop input stacks. */ void input_reset( void ) { InputState *is = &input_state; #ifdef DEBUG_CHARACTER printf( "input_reset:\n" ); #endif /*DEBUG_CHARACTER*/ is->bwp = 0; is->bspsp = 0; is->bsp[0] = 0; vips_buf_rewind( &lex_text ); } void input_push( int n ) { InputState *is = &input_state; #ifdef DEBUG_CHARACTER printf( "input_push(%d): going to level %d, %d bytes into buffer\n", n, is->bspsp + 1, is->bwp ); { const int len = IM_MIN( is->bwp, 20 ); int i; for( i = is->bwp - len; i < is->bwp; i++ ) if( is->buf[i] == '\n' ) printf( "@" ); else if( is->buf[i] == ' ' || is->buf[i] == '\t' ) printf( "_" ); else printf( "%c", is->buf[i] ); printf( "\n" ); for( i = 0; i < len; i++ ) printf( "-" ); printf( "^\n" ); } #endif /*DEBUG_CHARACTER*/ is->bspsp += 1; if( is->bspsp >= MAX_SSTACK ) error( "bstack overflow" ); is->bsp[is->bspsp] = is->bwp; } /* Yuk! We've just done an input_push() to try to grab the RHS of a * definition ... unfortunately, due to token readahead, we've probably * already read the start of the RHS. * * Back up the start point to just after the last ch character. */ void input_backtoch( char ch ) { InputState *is = &input_state; int i; for( i = is->bsp[is->bspsp] - 1; i > 0 && is->buf[i] != ch; i-- ) ; if( is->buf[i] == ch ) is->bsp[is->bspsp] = i + 1; } /* Move the last input_push() point back 1 character. */ void input_back1( void ) { InputState *is = &input_state; if( is->bsp[is->bspsp] > 0 ) is->bsp[is->bspsp] -= 1; } void input_pop( void ) { InputState *is = &input_state; #ifdef DEBUG_CHARACTER printf( "input_pop: %d bytes into buffer\n", input_state.bwp ); #endif /*DEBUG_CHARACTER*/ if( is->bspsp <= 0 ) error( "bstack underflow" ); is->bspsp--; } void scope_push( void ) { if( scope_sp == MAX_SSTACK ) error( "sstack overflow" ); scope_stack_symbol[scope_sp] = current_symbol; scope_stack_compile[scope_sp] = current_compile; scope_sp += 1; } void scope_pop( void ) { if( scope_sp <= 0 ) error( "sstack underflow" ); scope_sp -= 1; current_symbol = scope_stack_symbol[scope_sp]; current_compile = scope_stack_compile[scope_sp]; } /* Back to the outermost scope. */ void scope_pop_all( void ) { if( scope_sp > 0 ) { scope_sp = 0; current_symbol = scope_stack_symbol[scope_sp]; current_compile = scope_stack_compile[scope_sp]; } } /* Reset/push/pop parser stacks. */ void scope_reset( void ) { scope_sp = 0; } /* End of top level parse. Fix up the symbol. */ void * parse_toplevel_end( Symbol *sym ) { Tool *tool; tool = tool_new_sym( current_kit, tool_position, sym ); tool->lineno = last_top_lineno; symbol_made( sym ); return( NULL ); } /* Built a pattern access definition. Set the various text fragments from the * def we are drived from. */ void * parse_access_end( Symbol *sym, Symbol *main ) { IM_SETSTR( sym->expr->compile->rhstext, main->expr->compile->rhstext ); IM_SETSTR( sym->expr->compile->prhstext, main->expr->compile->prhstext ); IM_SETSTR( sym->expr->compile->text, main->expr->compile->text ); return( NULL ); } /* Interface to parser. */ static gboolean parse_input( int ch, Symbol *sym, Toolkit *kit, int pos ) { current_kit = kit; current_symbol = sym; root_symbol = sym; tool_position = pos; scope_reset(); input_reset(); /* Signal start nonterminal to parser. */ ip_unput( ch ); if( setjmp( parse_error_point ) ) { /* Restore current_compile. */ scope_pop_all(); if( current_compile ) compile_error_set( current_compile ); return( FALSE ); } yyparse(); /* All ok. */ return( TRUE ); } /* Parse the input into a set of symbols at a position in a kit. * kit may be NULL for no kit. */ gboolean parse_toplevel( Toolkit *kit, int pos ) { gboolean result; current_compile = NULL; result = parse_input( ',', kit->kitg->root, kit, pos ); iobject_changed( IOBJECT( kit ) ); return( result ); } /* Parse a single top-level definition. */ gboolean parse_onedef( Toolkit *kit, int pos ) { gboolean result; current_compile = NULL; result = parse_input( '^', kit->kitg->root, kit, pos ); iobject_changed( IOBJECT( kit ) ); return( result ); } /* Parse new text into "expr". If params is set, str should be "a b = a+b" * (ie. include params), if not, then just rhs (eg. "a+b"). */ gboolean parse_rhs( Expr *expr, ParseRhsSyntax syntax ) { static const char start_ch_table[] = { '&', /* PARSE_RHS */ '*', /* PARSE_PARAMS */ '@' /* PARSE_SUPER */ }; char start_ch = start_ch_table[(int) syntax]; Compile *compile = compile_new_local( expr ); current_compile = compile; if( !parse_input( start_ch, expr->sym, NULL, -1 ) ) { current_compile = NULL; return( FALSE ); } current_compile = NULL; #ifdef DEBUG printf( "parse_rhs:\n" ); dump_tree( compile->tree ); #endif /*DEBUG*/ /* Resolve any dynamic refs. */ expr_resolve( expr ); /* Compile. */ if( compile_object( compile ) ) return( FALSE ); return( TRUE ); } /* Free any stuff the lexer might have allocated. */ void free_lex( int yychar ) { switch( yychar ) { case TK_CONST: tree_const_destroy( &yylval.yy_const ); break; case TK_IDENT: case TK_TAG: IM_FREE( yylval.yy_name ); break; default: break; } } /* Do we have a string of the form "IDENT = .."? Use the lexer to look along * the string checking components, return the IDENT if we do, NULL otherwise. */ char * parse_test_define( void ) { extern int yylex( void ); int yychar; char *ident; ident = NULL; if( setjmp( parse_error_point ) ) { /* Here for yyerror in lex. */ IM_FREE( ident ); return( NULL ); } if( (yychar = yylex()) != TK_IDENT ) { free_lex( yychar ); return( NULL ); } ident = yylval.yy_name; if( (yychar = yylex()) != '=' ) { free_lex( yychar ); IM_FREE( ident ); return( NULL ); } return( ident ); } /* Do we have a string like "Workspaces.untitled.A1 = .."? Check for the * symbols as we see them, make the last one and return it. Used by --set. */ Symbol * parse_set_symbol( void ) { int yychar; extern int yylex( void ); Compile *compile = symbol_root->expr->compile; char *ident; Symbol *sym; ident = NULL; if( setjmp( parse_error_point ) ) { /* Here for yyerror in lex. */ IM_FREE( ident ); return( NULL ); } do { if( (yychar = yylex()) != TK_IDENT && yychar != TK_TAG ) { free_lex( yychar ); yyerror( _( "identifier expected" ) ); } ident = yylval.yy_name; switch( (yychar = yylex()) ) { case '.': /* There's a dot, so we expect another identifier to * come. Look up this one and move to that context. */ if( !(sym = compile_lookup( compile, ident )) ) nip2yyerror( _( "'%s' does not exist" ), ident ); if( !sym->expr || !sym->expr->compile ) nip2yyerror( _( "'%s' has no members" ), ident ); compile = sym->expr->compile; IM_FREE( ident ); break; case '=': /* This is the final identifier: create the symbol in * this context. */ sym = symbol_new_defining( compile, ident ); IM_FREE( ident ); break; default: free_lex( yychar ); yyerror( _( "'.' or '=' expected" ) ); } } while( yychar != '=' ); return( sym ); } ================================================ FILE: src/parser.h ================================================ /* Global variables from parse.y. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Our input stream can be attached to either a string or a FILE. * Keep track of the state of play here. */ typedef struct { iOpenFile *of; /* Non-NULL if we read from a file */ char *str; /* Non-NULL if we read from a string */ char *strpos; /* Position in string */ char buf[MAX_STRSIZE]; /* Accumulate text of each definition here */ int bwp; /* Write point in the above */ int bsp[MAX_SSTACK]; /* Start point stack */ int bspsp; /* Stack pointer */ int lineno; /* Current line number */ int charno; /* Character in line */ int pcharno; /* Characters in previous line */ int charpos; /* Characters read by lex so far */ int oldchar; /* unget buffer, -1 for no unget */ } InputState; extern InputState input_state; /* Function declarations for parse.y. */ void nip2yyerror( const char *sub, ... ) __attribute__((format(printf, 1, 2))); void yyerror( const char *msg ); #ifdef YYLENG_IS_YY_SIZE_T /* Assume yy_size_t is size_t. */ extern size_t yyleng; #else extern int yyleng; /* lex stuff */ #endif /* Lex gathers tokens here for workspace.c */ extern VipsBuf lex_text; /* Attach input for lex. */ void attach_input_file( iOpenFile *of ); void attach_input_string( const char *str ); int ip_input( void ); void ip_unput( int ch ); void ip_unget( void ); gboolean is_EOF( void ); /* Parse stuff. */ /* Order and number important ... see table in parse_rhs() */ typedef enum { PARSE_RHS = 0, /* eg. "a + b" */ PARSE_PARAMS, /* eg. "a b = a + b" */ PARSE_SUPER /* eg. "fred c d" */ } ParseRhsSyntax; extern jmp_buf parse_error_point; gboolean parse_toplevel( Toolkit *kit, int pos ); gboolean parse_onedef( Toolkit *kit, int pos ); gboolean parse_rhs( Expr *expr, ParseRhsSyntax syntax ); void free_lex( int yychar ); char *parse_test_define( void ); Symbol *parse_set_symbol( void ); ================================================ FILE: src/path.c ================================================ /* Search paths for files. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* just load .defs/.wses from "." #define DEBUG_LOCAL */ /* show path searches #define DEBUG_SEARCH */ /* show path rewrites #define DEBUG_REWRITE */ #include "ip.h" /* Default search paths if prefs fail. */ GSList *path_start_default = NULL; GSList *path_search_default = NULL; const char *path_tmp_default = NULL; /* We rewrite paths to try to handle files referenced in workspaces in * directories that move. * * For example, suppose we have workspace.ws in /some/directory which loads * image.v in that directory. The workspace will include a line like * (Image_file "/some/directory/image"). Now if directory is moved to * /other/directory and workspace.ws reloaded, we want to rewrite the string * "/some/directory/image.v" to "/other/directory/image.v". * * Also consider picking ICC profiles in export/import: we want to avoid * putting the path into the ws file, we need to go back to "$VIPSHOME" again. * * Rewrite rules can be "locked". For example, we don't want the rewrite from * "/home/john" to "$HOME" to ever be removed. */ typedef struct _Rewrite { char *old; char *new; gboolean lock; } Rewrite; static GSList *rewrite_list = NULL; static void path_rewrite_free( Rewrite *rewrite ) { rewrite_list = g_slist_remove( rewrite_list, rewrite ); IM_FREE( rewrite->old ); IM_FREE( rewrite->new ); IM_FREE( rewrite ); } void path_rewrite_free_all( void ) { while( rewrite_list ) { Rewrite *rewrite = (Rewrite *) rewrite_list->data; IM_FREEF( path_rewrite_free, rewrite ); } } static Rewrite * path_rewrite_new( const char *old, const char *new, gboolean lock ) { Rewrite *rewrite; rewrite = g_new( Rewrite, 1 ); rewrite->old = g_strdup( old ); rewrite->new = g_strdup( new ); rewrite->lock = lock; rewrite_list = g_slist_prepend( rewrite_list, rewrite ); return( rewrite ); } static gint path_rewrite_sort_fn( Rewrite *a, Rewrite *b ) { return( strlen( b->old ) - strlen( a->old ) ); } static Rewrite * path_rewrite_lookup( const char *old ) { GSList *p; Rewrite *rewrite; for( p = rewrite_list; p; p = p->next ) { rewrite = (Rewrite *) p->data; if( strcmp( old, rewrite->old ) == 0 ) return( rewrite ); } return( NULL ); } /* Add a new rewrite pair to the rewrite list. @new can be NULL, meaning * remove a rewrite rule. */ void path_rewrite_add( const char *old, const char *new, gboolean lock ) { char old_buf[FILENAME_MAX + 1]; char new_buf[FILENAME_MAX + 1]; Rewrite *rewrite; #ifdef DEBUG_REWRITE printf( "path_rewrite_add: old = %s, new = %s, lock = %d\n", old, new, lock ); #endif /*DEBUG_REWRITE*/ g_return_if_fail( old ); /* We want the old path in long form, with a trailing '/'. The * trailing '/' will stop us rewriting filenames. * * If we keep all @old paths in long form we can avoid rewrite loops. */ im_strncpy( old_buf, old, FILENAME_MAX ); strcat( old_buf, G_DIR_SEPARATOR_S ); path_expand( old_buf ); old = old_buf; if( new ) { /* We must keep the new path in short (unexpanded) form, * obviously. */ im_strncpy( new_buf, new, FILENAME_MAX ); strcat( new_buf, G_DIR_SEPARATOR_S ); new = new_buf; } /* If old is a prefix of new we will get endless expansion. */ if( new && is_prefix( old, new ) ) return; if( (rewrite = path_rewrite_lookup( old )) ) { if( !rewrite->lock && (!new || strcmp( old, new ) == 0) ) { #ifdef DEBUG_REWRITE printf( "path_rewrite_add: removing\n" ); #endif /*DEBUG_REWRITE*/ IM_FREEF( path_rewrite_free, rewrite ); } else if( !rewrite->lock && new ) { #ifdef DEBUG_REWRITE printf( "path_rewrite_add: updating\n" ); #endif /*DEBUG_REWRITE*/ IM_SETSTR( rewrite->new, new ); } else { #ifdef DEBUG_REWRITE printf( "path_rewrite_add: rewrite rule locked\n" ); #endif /*DEBUG_REWRITE*/ } } else if( new && strcmp( old, new ) != 0 ) { #ifdef DEBUG_REWRITE printf( "path_rewrite_add: adding\n" ); #endif /*DEBUG_REWRITE*/ (void) path_rewrite_new( old, new, lock ); } /* Keep longest old first, in case one old is a prefix of * another. */ rewrite_list = g_slist_sort( rewrite_list, (GCompareFunc) path_rewrite_sort_fn ); #ifdef DEBUG_REWRITE { GSList *p; printf( "path_rewrite_add: state:\n" ); for( p = rewrite_list; p; p = p->next ) { rewrite = (Rewrite *) p->data; printf( "\told = %s, new = %s\n", rewrite->old, rewrite->new ); } } #endif /*DEBUG_REWRITE*/ } /* Rewrite a string using the rewrite list. buf must be FILENAME_MAX * characters. */ void path_rewrite( char *buf ) { GSList *p; gboolean changed; #ifdef DEBUG_REWRITE printf( "path_rewrite: %s\n", buf ); #endif /*DEBUG_REWRITE*/ do { changed = FALSE; for( p = rewrite_list; p; p = p->next ) { Rewrite *rewrite = (Rewrite *) p->data; if( is_prefix( rewrite->old, buf ) ) { int olen = strlen( rewrite->old ); int nlen = strlen( rewrite->new ); int blen = strlen( buf ); if( blen - olen + nlen > FILENAME_MAX - 3 ) break; memmove( buf + nlen, buf + olen, blen - olen + 1 ); memcpy( buf, rewrite->new, nlen ); changed = TRUE; break; } } } while( changed ); #ifdef DEBUG_REWRITE printf( "\t-> %s\n", buf ); #endif /*DEBUG_REWRITE*/ } /* The inverse: rewrite in long form ready for file ops. */ void path_expand( char *path ) { char buf[FILENAME_MAX]; expand_variables( path, buf ); nativeize_path( buf ); absoluteize_path( buf ); canonicalize_path( buf ); im_strncpy( path, buf, FILENAME_MAX ); } /* Rewite a path to compact form. @path must be FILENAME_MAX characters. * * Examples: * * /home/john/../somefile -> $HOME/../somefile * /home/./john/../somefile -> $HOME/../somefile * fred -> ./fred */ void path_compact( char *path ) { path_expand( path ); path_rewrite( path ); } /* Turn a search path (eg. "/pics/lr:/pics/hr") into a list of directory names. */ GSList * path_parse( const char *path ) { GSList *op = NULL; const char *p; const char *e; int len; char name[FILENAME_MAX + 1]; for( p = path; *p; p = e ) { /* Find the start of the next component, or the NULL * character. */ if( !(e = strchr( p, G_SEARCHPATH_SEPARATOR )) ) e = p + strlen( p ); len = e - p + 1; /* Copy to our buffer, turn to string. */ im_strncpy( name, p, IM_MIN( len, FILENAME_MAX ) ); /* Add to path list. */ op = g_slist_append( op, im_strdupn( name ) ); /* Skip G_SEARCHPATH_SEPARATOR. */ if( *e == G_SEARCHPATH_SEPARATOR ) e++; } return( op ); } /* Free a path. path_free() is reserved n OS X :( */ void path_free2( GSList *path ) { slist_map( path, (SListMapFn) im_free, NULL ); g_slist_free( path ); } /* Sub-fn of below. Add length of this string + 1 (for ':'). */ static int path_add_component( const char *str, int c ) { return( c + strlen( str ) + 1 ); } /* Sub-fn of below. Copy string to buffer, append ':', return new end. */ static char * path_add_string( const char *str, char *buf ) { strcpy( buf, str ); strcat( buf, G_SEARCHPATH_SEPARATOR_S ); return( buf + strlen( buf ) ); } /* Turn a list of directory names into a search path. */ char * path_unparse( GSList *path ) { int len = GPOINTER_TO_INT( slist_fold( path, 0, (SListFoldFn) path_add_component, NULL ) ); char *buf = imalloc( NULL, len + 1 ); /* Build new string. */ slist_fold( path, buf, (SListFoldFn) path_add_string, NULL ); /* Fix '\0' to remove trailing G_SEARCHPATH_SEPARATOR. */ if( len > 0 ) buf[len - 1] = '\0'; return( buf ); } /* Track this stuff during a file search. */ typedef struct _Search { /* Pattern we search for, and it's compiled form. This does not * include any directory components. */ char *basename; GPatternSpec *wild; /* Directory offset. If the original pattern is a relative path, eg. * "poop/x*.v", we search every directory on path for a subdirectory * called "poop" and then search all files within that. */ char *dirname; /* User function to call for every matching file. */ path_map_fn fn; void *a; /* Files we've previously offered to the user function: we remove * duplicates. So "path1/wombat.def" hides "path2/wombat.def". */ GSList *previous; } Search; static void path_search_free( Search *search ) { IM_FREEF( g_free, search->basename ); IM_FREEF( g_free, search->dirname ); IM_FREEF( slist_free_all, search->previous ); IM_FREEF( g_pattern_spec_free, search->wild ); } static gboolean path_search_init( Search *search, const char *patt, path_map_fn fn, void *a ) { search->basename = g_path_get_basename( patt ); search->dirname = g_path_get_dirname( patt ); search->wild = NULL; search->fn = fn; search->a = a; search->previous = NULL; if( !(search->wild = g_pattern_spec_new( search->basename )) ) { path_search_free( search ); return( FALSE ); } return( TRUE ); } static void * path_str_eq( const char *s1, const char *s2 ) { if( strcmp( s1, s2 ) == 0 ) return( (void *) s1 ); else return( NULL ); } /* Test for string matches pattern. If the match is successful, call a user * function. */ static void * path_search_match( Search *search, const char *dir_name, const char *name ) { if( g_pattern_match_string( search->wild, name ) && !slist_map( search->previous, (SListMapFn) path_str_eq, (gpointer) name ) ) { char buf[FILENAME_MAX + 10]; void *result; /* Add to exclusion list. */ search->previous = g_slist_prepend( search->previous, g_strdup( name ) ); im_snprintf( buf, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "%s", dir_name, name ); path_compact( buf ); #ifdef DEBUG_SEARCH printf( "path_search_match: matched \"%s\"\n", buf ); #endif /*DEBUG_SEARCH*/ if( (result = search->fn( buf, search->a, NULL, NULL )) ) return( result ); } return( NULL ); } /* Scan a directory, calling a function for every entry. Abort scan if * function returns non-NULL. */ static void * path_scan_dir( const char *dir_name, Search *search ) { char buf[FILENAME_MAX]; GDir *dir; const char *name; void *result; /* Add the pattern offset, if any. It's '.' for no offset. */ im_snprintf( buf, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "%s", dir_name, search->dirname ); if( !(dir = (GDir *) callv_string_filename( (callv_string_fn) g_dir_open, buf, NULL, NULL, NULL )) ) return( NULL ); while( (name = g_dir_read_name( dir )) ) if( (result = path_search_match( search, buf, name )) ) { g_dir_close( dir ); return( result ); } g_dir_close( dir ); return( NULL ); } /* Scan a search path, applying a function to every file name which matches a * pattern. If the user function returns NULL, keep looking, otherwise return * its result. We return NULL on error, or if the user function returns NULL * for all filenames which match. * * Remove duplicates: if fred.wombat is in the first and second dirs on the * path, only apply to the first occurence. FIXME ... speed up with a hash and a (date based) cache at some point */ void * path_map( GSList *path, const char *patt, path_map_fn fn, void *a ) { Search search; void *result; #ifdef DEBUG_SEARCH printf( "path_map: searching for \"%s\"\n", patt ); #endif /*DEBUG_SEARCH*/ if( !path_search_init( &search, patt, fn, a ) ) return( NULL ); result = slist_map( path, (SListMapFn) path_scan_dir, &search ); path_search_free( &search ); return( result ); } /* As above, but scan a single directory. */ void * path_map_dir( const char *dir, const char *patt, path_map_fn fn, void *a ) { Search search; void *result; #ifdef DEBUG_SEARCH printf( "path_map_dir: searching for \"%s\"\n", patt ); #endif /*DEBUG_SEARCH*/ if( !path_search_init( &search, patt, fn, a ) ) return( NULL ); if( !(result = path_scan_dir( dir, &search )) ) { /* Not found? Maybe - error message anyway. */ error_top( _( "Not found." ) ); error_sub( _( "File \"%s\" not found." ), patt ); } path_search_free( &search ); return( result ); } /* Search for a file on the search path. */ char * path_find_file( const char *filename ) { char *fname; #ifdef DEBUG_SEARCH printf( "path_find_file: \"%s\"\n", filename ); #endif /*DEBUG_SEARCH*/ /* Try file name exactly. */ if( existsf( "%s", filename ) ) return( im_strdupn( filename ) ); /* Search everywhere. */ if( (fname = path_map( PATH_SEARCH, filename, (path_map_fn) im_strdupn, NULL )) ) return( fname ); error_top( _( "Not found." ) ); error_sub( _( "File \"%s\" not found on path" ), filename ); return( NULL ); } void path_init( void ) { char buf[FILENAME_MAX]; path_rewrite_add( get_prefix(), "$VIPSHOME", TRUE ); path_rewrite_add( g_get_home_dir(), "$HOME", TRUE ); path_rewrite_add( get_savedir(), "$SAVEDIR", TRUE ); /* You might think we could add a rule to swap '.' for * g_get_current_dir(), but that would then make workspaces depend on * a certain value of cwd before they could work. */ /* And the expanded form too. */ expand_variables( get_savedir(), buf ); path_rewrite_add( buf, "$SAVEDIR", TRUE ); #ifdef DEBUG_LOCAL printf( "path_init: loading start from \".\" only\n" ); path_start_default = path_parse( "." ); path_search_default = path_parse( "." ); path_tmp_default = im_strdup( NULL, "." ); #else /*!DEBUG_LOCAL*/ im_snprintf( buf, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "start" G_SEARCHPATH_SEPARATOR_S "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "$PACKAGE" G_DIR_SEPARATOR_S "start", get_savedir() ); path_start_default = path_parse( buf ); im_snprintf( buf, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "data" G_SEARCHPATH_SEPARATOR_S "$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "$PACKAGE" G_DIR_SEPARATOR_S "data" G_SEARCHPATH_SEPARATOR_S ".", get_savedir() ); path_search_default = path_parse( buf ); im_snprintf( buf, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "tmp", get_savedir() ); path_tmp_default = im_strdup( NULL, buf ); #endif /*DEBUG_LOCAL*/ } ================================================ FILE: src/path.h ================================================ /* Declarations supporting search.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ extern GSList *path_search_default; extern GSList *path_start_default; extern const char *path_tmp_default; /* Type of path_map functions. */ typedef void *(*path_map_fn)( const char *, void *, void *, void * ); void path_rewrite_free_all( void ); void path_rewrite_add( const char *old, const char *new, gboolean lock ); void path_rewrite( char *buf ); void path_compact( char *path ); void path_expand( char *path ); char *path_rewrite_file( const char *patt ); GSList *path_parse( const char *path ); char *path_unparse( GSList *path ); void path_free2( GSList *path ); void *path_map( GSList *path, const char *patt, path_map_fn fn, void *a ); void *path_map_dir( const char *dir, const char *patt, path_map_fn fn, void *a ); char *path_find_file( const char *patt ); void path_init( void ); ================================================ FILE: src/pathname.c ================================================ /* an input pathname ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void pathname_dispose( GObject *gobject ) { Pathname *pathname = PATHNAME( gobject ); #ifdef DEBUG printf( "pathname_dispose\n" ); #endif /*DEBUG*/ IM_FREE( pathname->value ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static View * pathname_view_new( Model *model, View *parent ) { return( pathnameview_new() ); } static void * pathname_update_model( Heapmodel *heapmodel ) { #ifdef DEBUG printf( "pathname_update_model\n" ); #endif /*DEBUG*/ if( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ) return( heapmodel ); return( NULL ); } /* Members of pathname we automate. */ static ClassmodelMember pathname_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( iObject, caption ) }, { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Pathname, value ) } }; static void pathname_class_init( PathnameClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = pathname_dispose; model_class->view_new = pathname_view_new; heapmodel_class->update_model = pathname_update_model; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = pathname_members; classmodel_class->n_members = IM_NUMBER( pathname_members ); } static void pathname_init( Pathname *pathname ) { /* Overridden later. Just something sensible. */ pathname->value = NULL; IM_SETSTR( pathname->value, "no-file" ); iobject_set( IOBJECT( pathname ), CLASS_PATHNAME, NULL ); } GType pathname_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PathnameClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) pathname_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Pathname ), 32, /* n_preallocs */ (GInstanceInitFunc) pathname_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Pathname", &info, 0 ); } return( type ); } ================================================ FILE: src/pathname.h ================================================ /* a pathname in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PATHNAME (pathname_get_type()) #define PATHNAME( obj ) (GTK_CHECK_CAST( (obj), TYPE_PATHNAME, Pathname )) #define PATHNAME_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PATHNAME, PathnameClass )) #define IS_PATHNAME( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PATHNAME )) #define IS_PATHNAME_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PATHNAME )) typedef struct _Pathname { Classmodel model; char *value; } Pathname; typedef struct _PathnameClass { ClassmodelClass parent_class; /* My methods. */ } PathnameClass; GType pathname_get_type( void ); ================================================ FILE: src/pathnameview.c ================================================ /* run the display for an arrow in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; static void pathnameview_link( View *view, Model *model, View *parent ) { Pathnameview *pathnameview = PATHNAMEVIEW( view ); VIEW_CLASS( parent_class )->link( view, model, parent ); if( GRAPHICVIEW( view )->sview ) gtk_size_group_add_widget( GRAPHICVIEW( view )->sview->group, pathnameview->label ); } static void pathnameview_refresh( vObject *vobject ) { Pathnameview *pathnameview = PATHNAMEVIEW( vobject ); Pathname *pathname = PATHNAME( VOBJECT( vobject )->iobject ); #ifdef DEBUG printf( "pathnameview_refresh: " ); row_name_print( HEAPMODEL( pathname )->row ); printf( "\n" ); #endif /*DEBUG*/ if( vobject->iobject->caption ) set_glabel( pathnameview->label, _( "%s:" ), vobject->iobject->caption ); if( pathname->value ) gtk_button_set_label( GTK_BUTTON( pathnameview->button ), im_skip_dir( pathname->value ) ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void pathnameview_class_init( PathnameviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = pathnameview_refresh; view_class->link = pathnameview_link; } static void pathnameview_edit_ok( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Pathname *pathname = PATHNAME( client ); char *fname; if( (fname = filesel_get_filename( filesel )) ) { IM_SETSTR( pathname->value, fname ); classmodel_update( CLASSMODEL( pathname ) ); symbol_recalculate_all(); g_free( fname ); nfn( sys, IWINDOW_YES ); } else nfn( sys, IWINDOW_ERROR ); } static void pathnameview_clicked_cb( GtkWidget *widget, Pathnameview *pathnameview ) { Pathname *pathname = PATHNAME( VOBJECT( pathnameview )->iobject ); GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), "%s", IOBJECT( pathname )->caption ); filesel_set_flags( FILESEL( filesel ), TRUE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_any, 0 ); iwindow_set_parent( IWINDOW( filesel ), widget ); idialog_set_iobject( IDIALOG( filesel ), IOBJECT( pathname ) ); filesel_set_done( FILESEL( filesel ), pathnameview_edit_ok, pathname ); iwindow_build( IWINDOW( filesel ) ); filesel_set_filename( FILESEL( filesel ), pathname->value ); gtk_widget_show( GTK_WIDGET( filesel ) ); } static void pathnameview_init( Pathnameview *pathnameview ) { GtkWidget *hbox; #ifdef DEBUG printf( "pathnameview_init\n" ); #endif /*DEBUG*/ hbox = gtk_hbox_new( FALSE, 12 ); gtk_box_pack_start( GTK_BOX( pathnameview ), hbox, TRUE, FALSE, 0 ); pathnameview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( pathnameview->label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( pathnameview->label ), 2, 7 ); gtk_box_pack_start( GTK_BOX( hbox ), pathnameview->label, FALSE, FALSE, 2 ); pathnameview->button = gtk_button_new_with_label( "" ); gtk_box_pack_start( GTK_BOX( hbox ), pathnameview->button, TRUE, TRUE, 0 ); gtk_signal_connect( GTK_OBJECT( pathnameview->button ), "clicked", GTK_SIGNAL_FUNC( pathnameview_clicked_cb ), pathnameview ); set_tooltip( pathnameview->button, _( "Select a new file name" ) ); gtk_widget_show_all( GTK_WIDGET( hbox ) ); } GtkType pathnameview_get_type( void ) { static GtkType pathnameview_type = 0; if( !pathnameview_type ) { static const GtkTypeInfo info = { "Pathnameview", sizeof( Pathnameview ), sizeof( PathnameviewClass ), (GtkClassInitFunc) pathnameview_class_init, (GtkObjectInitFunc) pathnameview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; pathnameview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( pathnameview_type ); } View * pathnameview_new( void ) { Pathnameview *pathnameview = gtk_type_new( TYPE_PATHNAMEVIEW ); return( VIEW( pathnameview ) ); } ================================================ FILE: src/pathnameview.h ================================================ /* a pathname view in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PATHNAMEVIEW (pathnameview_get_type()) #define PATHNAMEVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PATHNAMEVIEW, Pathnameview )) #define PATHNAMEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PATHNAMEVIEW, PathnameviewClass )) #define IS_PATHNAMEVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PATHNAMEVIEW )) #define IS_PATHNAMEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PATHNAMEVIEW )) typedef struct _Pathnameview { Graphicview parent_object; GtkWidget *label; GtkWidget *button; } Pathnameview; typedef struct _PathnameviewClass { GraphicviewClass parent_class; /* My methods. */ } PathnameviewClass; GtkType pathnameview_get_type( void ); View *pathnameview_new( void ); ================================================ FILE: src/plot.c ================================================ /* an input plot */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void plot_free_columns( Plot *plot ) { int i; for( i = 0; i < plot->columns; i++ ) { IM_FREE( plot->xcolumn[i] ); IM_FREE( plot->ycolumn[i] ); } IM_FREE( plot->xcolumn ); IM_FREE( plot->ycolumn ); plot->columns = 0; plot->rows = 0; } static void plot_finalize( GObject *gobject ) { Plot *plot; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_PLOT( gobject ) ); plot = PLOT( gobject ); #ifdef DEBUG printf( "plot_finalize\n" ); #endif /*DEBUG*/ /* My instance finalize stuff. */ image_value_destroy( &plot->value ); plot_free_columns( plot ); vips_buf_destroy( &plot->caption_buffer ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } char * plot_f2c( PlotFormat format ) { switch( format ) { case PLOT_FORMAT_YYYY: return( _( "YYYY" ) ); case PLOT_FORMAT_XYYY: return( _( "XYYY" ) ); case PLOT_FORMAT_XYXY: return( _( "XYXY" ) ); default: g_assert( 0 ); /* Keep gcc happy. */ return( 0 ); } } char * plot_s2c( PlotStyle style ) { switch( style ) { case PLOT_STYLE_POINT: return( _( "Point" ) ); case PLOT_STYLE_LINE: return( _( "Line" ) ); case PLOT_STYLE_SPLINE: return( _( "Spline" ) ); case PLOT_STYLE_BAR: return( _( "Bar" ) ); default: g_assert( 0 ); /* Keep gcc happy. */ return( 0 ); } } static const char * plot_generate_caption( iObject *iobject ) { Plot *plot = PLOT( iobject ); VipsBuf *buf = &plot->caption_buffer; vips_buf_rewind( buf ); image_value_caption( &plot->value, buf ); vips_buf_appendf( buf, ", %d series, %d points", plot->columns, plot->rows ); vips_buf_appendf( buf, ", xrange [%g, %g]", plot->xmin, plot->xmax ); vips_buf_appendf( buf, ", yrange [%g, %g]", plot->ymin, plot->ymax ); return( vips_buf_all( buf ) ); } /* Unpack all data formats to XYXYXY. * * FIXME ... could save mem by reusing columns of Xes in YYYY and XYYY * cases */ static gboolean plot_unpack( Plot *plot, DOUBLEMASK *mask ) { int rows, columns; int r, c; double xmin, xmax; double ymin, ymax; rows = mask->ysize; switch( plot->format ) { case PLOT_FORMAT_YYYY: columns = mask->xsize; break; case PLOT_FORMAT_XYYY: if( mask->xsize < 2 ) { error_top( _( "Bad value." ) ); error_sub( _( "More than one column " "needed or XY plots" ) ); return( FALSE ); } columns = mask->xsize - 1; break; case PLOT_FORMAT_XYXY: if( (mask->xsize & 1) != 0 ) { error_top( _( "Bad value." ) ); error_sub( _( "Even number of columns only for " "XY format plots" ) ); return( FALSE ); } columns = mask->xsize / 2; break; default: columns = 1; g_assert( 0 ); } if( plot->columns != columns || plot->rows != rows ) { plot_free_columns( plot ); plot->xcolumn = IM_ARRAY( NULL, columns, double * ); plot->ycolumn = IM_ARRAY( NULL, columns, double * ); if( !plot->xcolumn || !plot->ycolumn ) { plot_free_columns( plot ); return( FALSE ); } plot->columns = columns; plot->rows = rows; for( c = 0; c < columns; c++ ) { plot->xcolumn[c] = NULL; plot->ycolumn[c] = NULL; } for( c = 0; c < columns; c++ ) { plot->xcolumn[c] = IM_ARRAY( NULL, rows, double ); plot->ycolumn[c] = IM_ARRAY( NULL, rows, double ); if( !plot->xcolumn[c] || !plot->ycolumn[c] ) { plot_free_columns( plot ); return( FALSE ); } } } switch( plot->format ) { case PLOT_FORMAT_YYYY: for( c = 0; c < columns; c++ ) for( r = 0; r < rows; r++ ) { plot->xcolumn[c][r] = r; plot->ycolumn[c][r] = mask->coeff[c + r * mask->xsize]; } break; case PLOT_FORMAT_XYYY: for( c = 0; c < columns; c++ ) for( r = 0; r < rows; r++ ) { plot->xcolumn[c][r] = mask->coeff[r * mask->xsize]; plot->ycolumn[c][r] = mask->coeff[c + 1 + r * mask->xsize]; } break; case PLOT_FORMAT_XYXY: for( c = 0; c < columns; c++ ) for( r = 0; r < rows; r++ ) { plot->xcolumn[c][r] = mask->coeff[c * 2 + r * mask->xsize]; plot->ycolumn[c][r] = mask->coeff[c * 2 + 1 + r * mask->xsize]; } break; default: g_assert( 0 ); } xmin = plot->xcolumn[0][0]; xmax = plot->xcolumn[0][0]; ymin = plot->ycolumn[0][0]; ymax = plot->ycolumn[0][0]; for( c = 0; c < columns; c++ ) for( r = 0; r < rows; r++ ) { if( plot->xcolumn[c][r] > xmax ) xmax = plot->xcolumn[c][r]; if( plot->xcolumn[c][r] < xmin ) xmin = plot->xcolumn[c][r]; if( plot->ycolumn[c][r] > ymax ) ymax = plot->ycolumn[c][r]; if( plot->ycolumn[c][r] < ymin ) ymin = plot->ycolumn[c][r]; } if( plot->xmin == PLOT_RANGE_UNSET ) plot->xmin = xmin; if( plot->xmax == PLOT_RANGE_UNSET ) plot->xmax = xmax; if( plot->ymin == PLOT_RANGE_UNSET ) plot->ymin = ymin; if( plot->ymax == PLOT_RANGE_UNSET ) plot->ymax = ymax; return( TRUE ); } #ifdef HAVE_LIBGOFFICE static View * plot_view_new( Model *model, View *parent ) { return( plotview_new() ); return( NULL ); } #endif /*HAVE_LIBGOFFICE*/ static void plot_edit( GtkWidget *parent, Model *model ) { #ifdef HAVE_LIBGOFFICE Plot *plot = PLOT( model ); Plotwindow *plotwindow; plotwindow = plotwindow_new( plot, parent ); gtk_widget_show( GTK_WIDGET( plotwindow ) ); #endif /*HAVE_LIBGOFFICE*/ } static xmlNode * plot_save( Model *model, xmlNode *xnode ) { Plot *plot = PLOT( model ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( !set_iprop( xthis, "plot_left", plot->left ) || !set_iprop( xthis, "plot_top", plot->top ) || !set_iprop( xthis, "plot_mag", plot->mag ) || !set_sprop( xthis, "show_status", bool_to_char( plot->show_status ) ) ) return( NULL ); return( xthis ); } static gboolean plot_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { Plot *plot = PLOT( model ); g_assert( IS_RHS( parent ) ); (void) get_iprop( xnode, "plot_left", &plot->left ); (void) get_iprop( xnode, "plot_top", &plot->top ); (void) get_iprop( xnode, "plot_mag", &plot->mag ); (void) get_bprop( xnode, "show_status", &plot->show_status ); return( MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ); } /* Members of plot we automate. */ static ClassmodelMember plot_options[] = { { CLASSMODEL_MEMBER_ENUM, NULL, PLOT_FORMAT_LAST - 1, "format", "format", N_( "Format" ), G_STRUCT_OFFSET( Plot, format ) }, { CLASSMODEL_MEMBER_ENUM, NULL, PLOT_STYLE_LAST - 1, "style", "style", N_( "Style" ), G_STRUCT_OFFSET( Plot, style ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, "xmin", "xmin", N_( "Xmin" ), G_STRUCT_OFFSET( Plot, xmin ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, "xmax", "xmax", N_( "Xmax" ), G_STRUCT_OFFSET( Plot, xmax ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, "ymin", "ymin", N_( "Ymin" ), G_STRUCT_OFFSET( Plot, ymin ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, "ymax", "ymax", N_( "Ymax" ), G_STRUCT_OFFSET( Plot, ymax ) }, { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( Plot, caption ) }, { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_XCAPTION, "xcaption", N_( "X Axis Caption" ), G_STRUCT_OFFSET( Plot, xcaption ) }, { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_YCAPTION, "ycaption", N_( "Y Axis Caption" ), G_STRUCT_OFFSET( Plot, ycaption ) }, { CLASSMODEL_MEMBER_STRING_LIST, NULL, 0, MEMBER_SERIES_CAPTIONS, "series_captions", N_( "Series Captions" ), G_STRUCT_OFFSET( Plot, series_captions ) } }; static ClassmodelMember plot_members[] = { { CLASSMODEL_MEMBER_OPTIONS, &plot_options, IM_NUMBER( plot_options ), MEMBER_OPTIONS, NULL, N_( "Options" ), 0 }, { CLASSMODEL_MEMBER_IMAGE, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Plot, value ) } }; /* Come here after we've read in new values from the heap. */ static gboolean plot_class_get( Classmodel *classmodel, PElement *root ) { Plot *plot = PLOT( classmodel ); ImageValue *value = &plot->value; IMAGE *im = imageinfo_get( FALSE, value->ii ); Imageinfo *ii2; IMAGE *t; DOUBLEMASK *mask; int (*fn)(VipsImage *, VipsImage *); /* nx1 or 1xm images only ... use Bands for columns. */ if( im->Xsize != 1 && im->Ysize != 1 ) { error_top( _( "Bad value." ) ); error_sub( _( "1xn or nx1 images only for Plot" ) ); return( FALSE ); } /* Don't ref this and it'll be removed on the next GC. */ if( !(ii2 = imageinfo_new_temp( main_imageinfogroup, reduce_context->heap, NULL, "p" )) ) return( FALSE ); t = imageinfo_get( FALSE, ii2 ); /* Rotate so that our mask will be in the correct orientation. */ if( im->Ysize == 1 ) fn = im_rot90; else fn = im_copy; if( fn( im, t ) ) { error_top( _( "Bad value." ) ); error_sub( _( "Unable to prepare image." ) ); error_vips(); return( FALSE ); } /* Unpack the image to a dmask, then unpack the dmask into a set of XY * columns. * * FIXME ... yuk! */ if( !(mask = im_vips2mask( t, "plot_class_get" )) ) { error_top( _( "Bad value." ) ); error_sub( _( "1xn or nx1 images only" ) ); error_vips(); return( FALSE ); } if( !plot_unpack( plot, mask ) ) { im_free_dmask( mask ); return( FALSE ); } im_free_dmask( mask ); return( TRUE ); } static void plot_reset( Classmodel *classmodel ) { Plot *plot = PLOT( classmodel ); image_value_destroy( &plot->value ); plot->format = PLOT_FORMAT_YYYY; plot->style = PLOT_STYLE_LINE; plot->xmin = PLOT_RANGE_UNSET; plot->xmax = PLOT_RANGE_UNSET; plot->ymin = PLOT_RANGE_UNSET; plot->ymax = PLOT_RANGE_UNSET; IM_SETSTR( plot->caption, NULL ); IM_SETSTR( plot->xcaption, NULL ); IM_SETSTR( plot->ycaption, NULL ); IM_FREEF( slist_free_all, plot->series_captions ); } static gboolean plot_graphic_save( Classmodel *classmodel, GtkWidget *parent, const char *filename ) { Plot *plot = PLOT( classmodel ); ImageValue *value = &plot->value; char buf[FILENAME_MAX]; expand_variables( filename, buf ); filesel_add_mode( buf ); if( value->ii ) if( !imageinfo_write( value->ii, buf ) ) return( FALSE ); mainw_recent_add( &mainw_recent_image, filename ); return( TRUE ); } static void plot_class_init( PlotClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = plot_finalize; iobject_class->generate_caption = plot_generate_caption; #ifdef HAVE_LIBGOFFICE model_class->view_new = plot_view_new; #endif /*HAVE_LIBGOFFICE*/ model_class->edit = plot_edit; model_class->save = plot_save; model_class->load = plot_load; classmodel_class->class_get = plot_class_get; classmodel_class->members = plot_members; classmodel_class->n_members = IM_NUMBER( plot_members ); classmodel_class->reset = plot_reset; classmodel_class->graphic_save = plot_graphic_save; classmodel_class->filetype = filesel_type_image; classmodel_class->filetype_pref = "IMAGE_FILE_TYPE"; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void plot_init( Plot *plot ) { #ifdef DEBUG printf( "plot_init\n" ); #endif /*DEBUG*/ image_value_init( &plot->value, CLASSMODEL( plot ) ); plot->xcolumn = NULL; plot->ycolumn = NULL; plot->rows = 0; plot->columns = 0; plot->show_status = FALSE; plot->mag = 100; plot->left = 0; plot->top = 0; vips_buf_init_dynamic( &plot->caption_buffer, MAX_LINELENGTH ); iobject_set( IOBJECT( plot ), CLASS_PLOT, NULL ); plot_reset( CLASSMODEL( plot ) ); } GtkType plot_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PlotClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) plot_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Plot ), 32, /* n_preallocs */ (GInstanceInitFunc) plot_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Plot", &info, 0 ); } return( type ); } #ifdef HAVE_LIBGOFFICE /* Make a GOColor from an RGB triple. Different versions of goffice have * different ways of doing this :( */ #ifdef GO_COLOR_FROM_RGB #define RGB( R, G, B ) GO_COLOR_FROM_RGB( R, G, B ) #else #define RGB( R, G, B ) RGB_TO_RGBA( RGB_TO_UINT( R, G, B ), 0xff ) #endif /* Choose line colours with this. RGB first, then mostly random. We can't use * goffice's default colours because we really want the first three to be: red, * green, blue. */ static GOColor default_colour[] = { RGB( 255, 0, 0 ), RGB( 0, 255, 0 ), RGB( 0, 0, 255 ), RGB( 100, 0, 102 ), RGB( 17, 0, 102 ), RGB( 0, 0, 180 ), RGB( 0, 53, 255 ), RGB( 0, 104, 234 ), RGB( 0, 150, 188 ), RGB( 0, 205, 170 ), RGB( 0, 255, 139 ), RGB( 0, 255, 55 ), RGB( 40, 255, 40 ), RGB( 106, 255, 74 ), RGB( 155, 255, 48 ), RGB( 209, 255, 21 ), RGB( 239, 255, 7 ), RGB( 255, 176, 0 ), RGB( 255, 110, 0 ), RGB( 255, 50, 0 ), RGB( 196, 0, 0 ) }; /* Build a GogPlot from a Plot. */ GogPlot * plot_new_gplot( Plot *plot ) { GogPlot *gplot; int i; if( plot->style == PLOT_STYLE_BAR ) gplot = gog_plot_new_by_name( "GogHistogramPlot" ); else gplot = gog_plot_new_by_name( "GogXYPlot" ); switch( plot->style ) { case PLOT_STYLE_POINT: g_object_set( gplot, "default-style-has-lines", FALSE, NULL ); break; case PLOT_STYLE_LINE: g_object_set( gplot, "default-style-has-markers", FALSE, NULL ); break; case PLOT_STYLE_SPLINE: g_object_set( gplot, "default-style-has-markers", FALSE, NULL ); g_object_set( gplot, "use-splines", TRUE, NULL ); break; case PLOT_STYLE_BAR: break; default: g_assert( FALSE ); } for( i = 0; i < plot->columns; i++ ) { GogSeries *series; GOData *data; GError *error; char *caption; series = gog_plot_new_series( gplot ); data = go_data_vector_val_new( plot->xcolumn[i], plot->rows, NULL ); gog_series_set_dim( series, 0, data, &error ); data = go_data_vector_val_new( plot->ycolumn[i], plot->rows, NULL ); gog_series_set_dim( series, 1, data, &error ); if( (caption = (char *) g_slist_nth_data( plot->series_captions, i )) ) caption = g_strdup( caption ); else caption = g_strdup_printf( "Band %d", i ); data = go_data_scalar_str_new( caption, TRUE ); gog_series_set_name( series, (GODataScalar *) data, &error ); if( i < IM_NUMBER( default_colour ) ) { GOStyle *style; style = go_styled_object_get_style( GO_STYLED_OBJECT( series ) ); style->line.color = default_colour[i]; style->line.auto_color = FALSE; go_marker_set_fill_color( style->marker.mark, default_colour[i] ); style->marker.auto_fill_color = FALSE; /* Could match fill, but black everywhere looks nicer. */ go_marker_set_outline_color( style->marker.mark, RGB( 0, 0, 0 ) ); style->marker.auto_outline_color = FALSE; gog_object_request_update( GOG_OBJECT( series ) ); } } return( gplot ); } static void plot_grid_add( GogAxis *axis ) { GogGridLine *ggl; if( !gog_object_get_child_by_name( GOG_OBJECT( axis ), "MajorGrid" ) ) { ggl = g_object_new( GOG_TYPE_GRID_LINE, "is-minor", FALSE, NULL ); gog_object_add_by_name( GOG_OBJECT( axis ), "MajorGrid", GOG_OBJECT( ggl ) ); } if( !gog_object_get_child_by_name( GOG_OBJECT( axis ), "MinorGrid" ) ) { ggl = g_object_new( GOG_TYPE_GRID_LINE, "is-minor", TRUE, NULL ); gog_object_add_by_name( GOG_OBJECT( axis ), "MinorGrid", GOG_OBJECT( ggl ) ); } g_object_set( axis, "pos", GOG_AXIS_CROSS, NULL ); } static void plot_set_title( GogObject *thing, const char *role, const char *text ) { GogObject *title; title = gog_object_get_child_by_name( thing, role ); if( text && !title ) { title = g_object_new( GOG_TYPE_LABEL, NULL ); gog_object_add_by_name( thing, role, title ); } else if( !text && title ) { gog_object_clear_parent( title ); UNREF( title ); } if( text && title ) { GOData *data; data = go_data_scalar_str_new( text, FALSE ); gog_dataset_set_dim( GOG_DATASET( title ), 0, data, NULL ); } } void plot_style_main( Plot *plot, GogChart *gchart ) { GSList *axes; GogAxis *axis; GogObject *legend; axes = gog_chart_get_axes( gchart, GOG_AXIS_X ); axis = GOG_AXIS( axes->data ); g_slist_free( axes ); gog_axis_set_bounds( axis, plot->xmin, plot->xmax ); plot_set_title( GOG_OBJECT( axis ), "Label", plot->xcaption ); plot_grid_add( axis ); axes = gog_chart_get_axes( gchart, GOG_AXIS_Y ); axis = GOG_AXIS( axes->data ); g_slist_free( axes ); gog_axis_set_bounds( axis, plot->ymin, plot->ymax ); plot_set_title( GOG_OBJECT( axis ), "Label", plot->ycaption ); plot_grid_add( axis ); legend = gog_object_get_child_by_name( GOG_OBJECT( gchart ), "Legend" ); if( plot->columns > 1 && !legend ) { legend = g_object_new( GOG_TYPE_LEGEND, NULL ); gog_object_add_by_name( GOG_OBJECT( gchart ), "Legend", GOG_OBJECT( legend ) ); } else if( plot->columns == 1 && legend ) { gog_object_clear_parent( legend ); UNREF( legend ); } plot_set_title( GOG_OBJECT( gchart ), "Title", plot->caption ); } void plot_style_thumbnail( Plot *plot, GogChart *gchart ) { GSList *axes; GogAxis *axis; axes = gog_chart_get_axes( gchart, GOG_AXIS_X ); axis = GOG_AXIS( axes->data ); g_slist_free( axes ); g_object_set( axis, "major-tick-labeled", FALSE, "major-tick-size-pts", 0, "pos", GOG_AXIS_CROSS, NULL ); gog_axis_set_bounds( axis, plot->xmin, plot->xmax ); axes = gog_chart_get_axes( gchart, GOG_AXIS_Y ); axis = GOG_AXIS( axes->data ); g_slist_free( axes ); g_object_set( axis, "major-tick-labeled", FALSE, "major-tick-size-pts", 0, "pos", GOG_AXIS_CROSS, NULL ); gog_axis_set_bounds( axis, plot->ymin, plot->ymax ); } Imageinfo * plot_to_image( Plot *plot, Reduce *rc, double dpi ) { GogGraph *ggraph; GogChart *gchart; GogPlot *gplot; GogRenderer *renderer; GdkPixbuf *pixbuf; double width_in_pts, height_in_pts; Imageinfo *ii; ggraph = g_object_new( GOG_TYPE_GRAPH, NULL ); gchart = g_object_new( GOG_TYPE_CHART, NULL ); gog_object_add_by_name( GOG_OBJECT( ggraph ), "Chart", GOG_OBJECT( gchart ) ); gplot = plot_new_gplot( plot ); gog_object_add_by_name( GOG_OBJECT( gchart ), "Plot", GOG_OBJECT( gplot ) ); plot_style_main( plot, gchart ); renderer = gog_renderer_new( ggraph ); gog_graph_force_update( ggraph ); gog_graph_get_size( ggraph, &width_in_pts, &height_in_pts); gog_renderer_update( renderer, width_in_pts * dpi / 72.0, height_in_pts * dpi / 72.0 ); pixbuf = gog_renderer_get_pixbuf( renderer ); if( !(ii = imageinfo_new_from_pixbuf( main_imageinfogroup, rc->heap, pixbuf )) ) { UNREF( renderer ); UNREF( ggraph ); return( NULL ); } /* Don't unref the pixbuf, we don't own it. */ UNREF( renderer ); UNREF( ggraph ); return( ii ); } #endif /*HAVE_LIBGOFFICE*/ ================================================ FILE: src/plot.h ================================================ /* a plot in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PLOT (plot_get_type()) #define PLOT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_PLOT, Plot )) #define PLOT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_PLOT, PlotClass)) #define IS_PLOT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_PLOT )) #define IS_PLOT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_PLOT )) #define PLOT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_PLOT, PlotClass )) typedef enum { PLOT_FORMAT_YYYY = 0, PLOT_FORMAT_XYYY, PLOT_FORMAT_XYXY, PLOT_FORMAT_LAST } PlotFormat; typedef enum { PLOT_STYLE_POINT = 0, PLOT_STYLE_LINE, PLOT_STYLE_SPLINE, PLOT_STYLE_BAR, PLOT_STYLE_LAST } PlotStyle; /* Magic number for 'range value unset' (ie. should auto-range). */ #define PLOT_RANGE_UNSET (-999999) struct _Plot { Classmodel model; /* Base class fields. */ ImageValue value; PlotFormat format; PlotStyle style; char *caption; char *xcaption; char *ycaption; GSList *series_captions; double xmin; double xmax; double ymin; double ymax; /* Unpack image to a set of xy columns here. */ double **xcolumn; double **ycolumn; int rows; int columns; /* Save x/y/mag/status here. Init plot windows from this, save and * load from workspaces. */ gboolean show_status; int mag; int left, top; /* Private ... build iobject caption here. */ VipsBuf caption_buffer; }; typedef struct _PlotClass { ClassmodelClass parent_class; /* My methods. */ } PlotClass; GType plot_get_type( void ); char *plot_f2c( PlotFormat format ); char *plot_s2c( PlotStyle style ); #ifdef HAVE_LIBGOFFICE GogPlot *plot_new_gplot( Plot *plot ); void plot_style_main( Plot *plot, GogChart *gchart ); void plot_style_thumbnail( Plot *plot, GogChart *gchart ); Imageinfo *plot_to_image( Plot *plot, Reduce *rc, double dpi ); #endif /*HAVE_LIBGOFFICE*/ ================================================ FILE: src/plotmodel.c ================================================ /* the model parts of a plot window .. all the window components watch this */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static iObjectClass *parent_class = NULL; static void plotmodel_dispose( GObject *gobject ) { Plotmodel *plotmodel; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_PLOTMODEL( gobject ) ); plotmodel = PLOTMODEL( gobject ); #ifdef DEBUG printf( "plotmodel_dispose %p: ", plotmodel ); iobject_print( IOBJECT( plotmodel ) ); #endif /*DEBUG*/ /* My instance destroy stuff. */ FREESID( plotmodel->changed_sid, plotmodel->plot ); FREESID( plotmodel->destroy_sid, plotmodel->plot ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void plotmodel_finalize( GObject *gobject ) { #ifdef DEBUG Plotmodel *plotmodel = PLOTMODEL( gobject ); printf( "plotmodel_finalize: %p\n", plotmodel ); #endif /*DEBUG*/ G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void plotmodel_changed( iObject *iobject ) { Plotmodel *plotmodel = PLOTMODEL( iobject ); #ifdef DEBUG printf( "plotmodel_changed:\n" ); #endif /*DEBUG*/ prefs_set( "DISPLAY_STATUS", "%s", bool_to_char( plotmodel->show_status ) ); IOBJECT_CLASS( parent_class )->changed( iobject ); } static void plotmodel_class_init( PlotmodelClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = plotmodel_dispose; gobject_class->finalize = plotmodel_finalize; iobject_class->changed = plotmodel_changed; /* Create signals. */ /* Init methods. */ } static void plotmodel_init( Plotmodel *plotmodel ) { #ifdef DEBUG printf( "plotmodel_init: %p\n", plotmodel ); #endif /*DEBUG*/ plotmodel->changed_sid = 0; plotmodel->destroy_sid = 0; plotmodel->width = -1; plotmodel->height = -1; plotmodel->mag = 100; plotmodel->show_status = DISPLAY_STATUS; } GType plotmodel_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PlotmodelClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) plotmodel_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Plotmodel ), 32, /* n_preallocs */ (GInstanceInitFunc) plotmodel_init, }; type = g_type_register_static( TYPE_IOBJECT, "Plotmodel", &info, 0 ); } return( type ); } static void plotmodel_plot_destroy_cb( Plot *plot, Plotmodel *plotmodel ) { plotmodel->plot = NULL; plotmodel->destroy_sid = 0; plotmodel->changed_sid = 0; } static void plotmodel_plot_changed_cb( Plot *plot, Plotmodel *plotmodel ) { iobject_changed( IOBJECT( plotmodel ) ); } static void plotmodel_link( Plotmodel *plotmodel, Plot *plot ) { /* Don't need to listen for "destroy": our enclosing Floatwindow does * that. */ plotmodel->plot = plot; plotmodel->destroy_sid = g_signal_connect( G_OBJECT( plot ), "destroy", G_CALLBACK( plotmodel_plot_destroy_cb ), plotmodel ); plotmodel->changed_sid = g_signal_connect( G_OBJECT( plot ), "changed", G_CALLBACK( plotmodel_plot_changed_cb ), plotmodel ); } Plotmodel * plotmodel_new( Plot *plot ) { Plotmodel *plotmodel = g_object_new( TYPE_PLOTMODEL, NULL ); plotmodel_link( plotmodel, plot ); return( plotmodel ); } void plotmodel_set_mag( Plotmodel *plotmodel, int mag ) { /* Don't let mag get too large or small. GtkPlotCanvas does not * display large magnifications at all well. */ mag = IM_CLIP( 100, mag, 800 ); if( plotmodel->mag != mag ) { #ifdef DEBUG printf( "plotmodel_set_mag: %d\n", mag ); #endif /*DEBUG*/ plotmodel->mag = mag; /* Invaidate width so the canvas is regenerated. */ plotmodel->width = -1; iobject_changed( IOBJECT( plotmodel ) ); } } void plotmodel_set_status( Plotmodel *plotmodel, gboolean show_status ) { if( plotmodel->show_status != show_status ) { #ifdef DEBUG printf( "plotmodel_set_status: %d\n", show_status ); #endif /*DEBUG*/ plotmodel->show_status = show_status; iobject_changed( IOBJECT( plotmodel ) ); } } ================================================ FILE: src/plotmodel.h ================================================ /* the model parts of a plot window .. all the window components watch this */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PLOTMODEL (plotmodel_get_type()) #define PLOTMODEL( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PLOTMODEL, Plotmodel )) #define PLOTMODEL_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PLOTMODEL, PlotmodelClass )) #define IS_PLOTMODEL( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PLOTMODEL )) #define IS_PLOTMODEL_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PLOTMODEL )) struct _Plotmodel { iObject parent_class; /* The class model we watch. */ Plot *plot; guint changed_sid; guint destroy_sid; /* The last canvas size we set ... stop resize loops with these. */ int width; int height; /* Viewer state. */ int mag; gboolean show_status; }; typedef struct _PlotmodelClass { iObjectClass parent_class; /* My methods. */ } PlotmodelClass; GtkType plotmodel_get_type( void ); Plotmodel *plotmodel_new( Plot *plot ); void plotmodel_set_mag( Plotmodel *plotmodel, int mag ); void plotmodel_set_status( Plotmodel *plotmodel, gboolean show_status ); ================================================ FILE: src/plotpresent.c ================================================ /* a plot widget, plus some navigation stuff */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_EVENT #define DEBUG */ #include "ip.h" #ifdef HAVE_LIBGOFFICE static GtkBinClass *parent_class = NULL; enum { SIG_MOUSE_MOVE, /* mose drag, axies cods */ SIG_LAST }; static guint plotpresent_signals[SIG_LAST] = { 0 }; static void plotpresent_mouse_move( Plotpresent *plotpresent, double x, double y ) { g_signal_emit( G_OBJECT( plotpresent ), plotpresent_signals[SIG_MOUSE_MOVE], 0, x, y ); } static void plotpresent_destroy( GtkObject *object ) { Plotpresent *plotpresent; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PLOTPRESENT( object ) ); plotpresent = PLOTPRESENT( object ); #ifdef DEBUG printf( "plotpresent_destroy: %p\n", plotpresent ); #endif /*DEBUG*/ /* My instance destroy stuff. */ UNREF( plotpresent->grend ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void plotpresent_size_request( GtkWidget *widget, GtkRequisition *requisition ) { GtkBin *bin = GTK_BIN( widget ); if( bin->child && GTK_WIDGET_VISIBLE( bin->child ) ) gtk_widget_size_request( bin->child, requisition ); } static void plotpresent_size_allocate( GtkWidget *widget, GtkAllocation *allocation ) { GtkBin *bin = GTK_BIN( widget ); if( bin->child && GTK_WIDGET_VISIBLE( bin->child ) ) gtk_widget_size_allocate( bin->child, allocation ); } /* Spot mouse motion events, to update status bar. */ static gboolean plotpresent_motion_notify_event( GtkWidget *widget, GdkEventMotion *event ) { Plotpresent *plotpresent = PLOTPRESENT( widget ); GtkAllocation *allocation = >K_WIDGET( plotpresent->canvas )->allocation; GogView *view; GSList *axes; GogAxis *x_axis; GogAxis *y_axis; GogChartMap *map; #ifdef DEBUG_EVENT printf( "plotpresent_motion_notify_event: %p\n", plotpresent ); printf( "event->x = %g, event->y = %g\n", event->x, event->y ); #endif /*DEBUG_EVENT*/ gog_renderer_update( plotpresent->grend, allocation->width, allocation->height ); g_object_get( G_OBJECT( plotpresent->grend ), "view", &view, NULL ); view = gog_view_find_child_view( view, GOG_OBJECT( plotpresent->gplot ) ); axes = gog_chart_get_axes( plotpresent->gchart, GOG_AXIS_X ); x_axis = GOG_AXIS( axes->data ); g_slist_free( axes ); axes = gog_chart_get_axes( plotpresent->gchart, GOG_AXIS_Y ); y_axis = GOG_AXIS( axes->data ); g_slist_free( axes ); map = gog_chart_map_new( plotpresent->gchart, &(view->allocation), x_axis, y_axis, NULL, FALSE ); if( gog_chart_map_is_valid( map ) && event->x >= view->allocation.x && event->x < view->allocation.x + view->allocation.w && event->y >= view->allocation.y && event->y < view->allocation.y + view->allocation.h ) { GogAxisMap *x_map; GogAxisMap *y_map; double x; double y; x_map = gog_chart_map_get_axis_map( map, 0 ); y_map = gog_chart_map_get_axis_map( map, 1 ); x = gog_axis_map_from_view( x_map, event->x ); y = gog_axis_map_from_view( y_map, event->y ); plotpresent_mouse_move( plotpresent, x, y ); } gog_chart_map_free( map ); return( FALSE ); } static void plotpresent_class_init( PlotpresentClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = plotpresent_destroy; widget_class->size_request = plotpresent_size_request; widget_class->size_allocate = plotpresent_size_allocate; widget_class->motion_notify_event = plotpresent_motion_notify_event; /* Create signals. */ plotpresent_signals[SIG_MOUSE_MOVE] = g_signal_new( "mouse_move", G_OBJECT_CLASS_TYPE( class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( PlotpresentClass, mouse_move ), NULL, NULL, nip_VOID__DOUBLE_DOUBLE, G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE ); /* Init methods. */ } static void plotpresent_init( Plotpresent *plotpresent ) { #ifdef DEBUG printf( "plotpresent_init: %p\n", plotpresent ); #endif /*DEBUG*/ plotpresent->gplot = NULL; plotpresent->canvas = go_graph_widget_new( NULL ); gtk_container_add( GTK_CONTAINER( plotpresent ), plotpresent->canvas ); gtk_widget_show( GTK_WIDGET( plotpresent->canvas ) ); plotpresent->ggraph = go_graph_widget_get_graph( GO_GRAPH_WIDGET( plotpresent->canvas ) ); plotpresent->gchart = go_graph_widget_get_chart( GO_GRAPH_WIDGET( plotpresent->canvas ) ); gtk_widget_add_events( plotpresent->canvas, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK ); /* You'd think we could set up the axies too, but we can't get them * from the chart until it's realized. Wait for the first refresh. */ plotpresent->grend = gog_renderer_new( plotpresent->ggraph ); } GType plotpresent_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PlotpresentClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) plotpresent_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Plotpresent ), 32, /* n_preallocs */ (GInstanceInitFunc) plotpresent_init, }; type = g_type_register_static( GTK_TYPE_BIN, "Plotpresent", &info, 0 ); } return( type ); } static void plotpresent_build_plot( Plotpresent *plotpresent ) { Plotmodel *plotmodel = plotpresent->plotmodel; Plot *plot = plotmodel->plot; #ifdef DEBUG printf( "plotpresent_build_plot: %p\n", plotpresent ); #endif /*DEBUG*/ GOG_UNREF( plotpresent->gplot ); plotpresent->gplot = plot_new_gplot( plot ); gog_object_add_by_name( GOG_OBJECT( plotpresent->gchart ), "Plot", GOG_OBJECT( plotpresent->gplot ) ); plot_style_main( plot, plotpresent->gchart ); } static void plotpresent_changed_cb( Plotmodel *plotmodel, Plotpresent *plotpresent ) { Plot *plot = plotmodel->plot; #ifdef DEBUG printf( "plotpresent_changed_cb: %p\n", plotpresent ); #endif /*DEBUG*/ /* Can refresh before model build. */ if( plot->rows == 0 || plot->columns == 0 ) return; /* Rebuild plot and data. */ plotpresent_build_plot( plotpresent ); } static void plotpresent_link( Plotpresent *plotpresent, Plotmodel *plotmodel ) { /* All the model parts for our set of views. */ plotpresent->plotmodel = plotmodel; g_signal_connect( G_OBJECT( plotmodel ), "changed", G_CALLBACK( plotpresent_changed_cb ), plotpresent ); } Plotpresent * plotpresent_new( Plotmodel *plotmodel ) { Plotpresent *plotpresent = gtk_type_new( TYPE_PLOTPRESENT ); plotpresent_link( plotpresent, plotmodel ); return( plotpresent ); } #endif /*HAVE_LIBGOFFICE*/ ================================================ FILE: src/plotpresent.h ================================================ /* a plot widget, plus some navigation stuff */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PLOTPRESENT (plotpresent_get_type()) #define PLOTPRESENT( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PLOTPRESENT, Plotpresent )) #define PLOTPRESENT_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PLOTPRESENT, PlotpresentClass )) #define IS_PLOTPRESENT( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PLOTPRESENT )) #define IS_PLOTPRESENT_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PLOTPRESENT )) struct _Plotpresent { GtkBin parent_class; /* Context. */ Plotmodel *plotmodel; /* Keep model parts of widgets here */ /* Widgets. */ GtkWidget *canvas; #ifdef HAVE_LIBGOFFICE GogRenderer *grend; GogChart *gchart; GogGraph *ggraph; GogPlot *gplot; #endif /*HAVE_LIBGOFFICE*/ }; typedef struct _PlotpresentClass { GtkBinClass parent_class; /* My methods. */ /* A mouse movement within the plot area. xy are in axies coordinates. */ void (*mouse_move)( Plotpresent *, double, double ); } PlotpresentClass; GtkType plotpresent_get_type( void ); Plotpresent *plotpresent_new( Plotmodel *plotmodel ); ================================================ FILE: src/plotstatus.c ================================================ /* widgets for the status bar */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GtkFrameClass *parent_class = NULL; /* The popup menu. */ static GtkWidget *plotstatus_menu = NULL; static void plotstatus_columns_destroy( Plotstatus *plotstatus ) { int i; for( i = 0; i < plotstatus->columns; i++ ) DESTROY_GTK( plotstatus->label[i] ); IM_FREE( plotstatus->label ); plotstatus->columns = 0; } static void plotstatus_destroy( GtkObject *object ) { Plotstatus *plotstatus; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PLOTSTATUS( object ) ); plotstatus = PLOTSTATUS( object ); #ifdef DEBUG printf( "plotstatus_destroy\n" ); #endif /*DEBUG*/ plotstatus_columns_destroy( plotstatus ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* Hide this plotstatus. */ static void plotstatus_hide_cb( GtkWidget *menu, GtkWidget *host, Plotstatus *plotstatus ) { plotmodel_set_status( plotstatus->plotmodel, FALSE ); } static void plotstatus_class_init( PlotstatusClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidget *pane; parent_class = g_type_class_peek_parent( class ); object_class->destroy = plotstatus_destroy; /* Create signals. */ /* Init methods. */ pane = plotstatus_menu = popup_build( _( "Status bar menu" ) ); popup_add_but( pane, GTK_STOCK_CLOSE, POPUP_FUNC( plotstatus_hide_cb ) ); } static void plotstatus_init( Plotstatus *plotstatus ) { GtkWidget *vb, *hb; GtkWidget *eb; plotstatus->plotmodel = NULL; plotstatus->label = NULL; plotstatus->columns = 0; gtk_frame_set_shadow_type( GTK_FRAME( plotstatus ), GTK_SHADOW_OUT ); eb = gtk_event_box_new(); gtk_container_add( GTK_CONTAINER( plotstatus ), eb ); popup_attach( eb, plotstatus_menu, plotstatus ); vb = gtk_vbox_new( FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( vb ), 1 ); gtk_container_add( GTK_CONTAINER( eb ), vb ); plotstatus->top = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( plotstatus->top ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( vb ), plotstatus->top, TRUE, TRUE, 0 ); hb = gtk_hbox_new( FALSE, 5 ); gtk_box_pack_start( GTK_BOX( vb ), hb, TRUE, TRUE, 0 ); plotstatus->pos = gtk_label_new( "" ); set_fixed( plotstatus->pos, strlen( "(8888888,8888888)" ) ); gtk_misc_set_alignment( GTK_MISC( plotstatus->pos ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( hb ), plotstatus->pos, FALSE, FALSE, 0 ); plotstatus->hb = gtk_hbox_new( FALSE, 5 ); gtk_box_pack_start( GTK_BOX( hb ), plotstatus->hb, TRUE, TRUE, 0 ); plotstatus->mag = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( plotstatus->mag ), 0.0, 0.5 ); gtk_box_pack_end( GTK_BOX( hb ), plotstatus->mag, FALSE, FALSE, 0 ); gtk_widget_show_all( eb ); } GtkType plotstatus_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Plotstatus", sizeof( Plotstatus ), sizeof( PlotstatusClass ), (GtkClassInitFunc) plotstatus_class_init, (GtkObjectInitFunc) plotstatus_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( GTK_TYPE_FRAME, &info ); } return( type ); } /* Model has changed: rebuild everything. */ static void plotstatus_refresh( Plotstatus *plotstatus ) { Plotmodel *plotmodel = plotstatus->plotmodel; Plot *plot = plotmodel->plot; #ifdef DEBUG printf( "plotstatus_refresh: %p\n", plotstatus ); printf( " show_status = %d\n", plotmodel->show_status ); #endif /*DEBUG*/ widget_visible( GTK_WIDGET( plotstatus ), plotmodel->show_status ); /* If we're hidden, no need to do any more. */ if( !plotmodel->show_status ) return; set_glabel( plotstatus->mag, "%s %d%%", _( "Magnification" ), plotmodel->mag ); set_gcaption( plotstatus->top, "%s", IOBJECT( plot )->caption ); if( plotstatus->columns != plot->columns ) { /* Bands/fmt has changed ... rebuild band display widgets. */ int columns; int i; /* Don't display more than 8 series ... it'll make the window * too large. FIXME ... not very kewl */ plotstatus_columns_destroy( plotstatus ); columns = IM_MIN( 8, plot->columns ); if( !(plotstatus->label = IM_ARRAY( NULL, columns, GtkWidget * )) ) return; for( i = 0; i < columns; i++ ) plotstatus->label[i] = NULL; plotstatus->columns = columns; for( i = 0; i < columns; i++ ) { GtkWidget *label; plotstatus->label[i] = label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); set_fixed( label, 8 ); gtk_box_pack_start( GTK_BOX( plotstatus->hb ), label, FALSE, FALSE, 0 ); gtk_widget_show( label ); } } } static void plotstatus_changed_cb( Plotmodel *plotmodel, Plotstatus *plotstatus ) { plotstatus_refresh( plotstatus ); } Plotstatus * plotstatus_new( Plotmodel *plotmodel ) { Plotstatus *plotstatus = gtk_type_new( TYPE_PLOTSTATUS ); plotstatus->plotmodel = plotmodel; g_signal_connect( G_OBJECT( plotmodel ), "changed", G_CALLBACK( plotstatus_changed_cb ), plotstatus ); return( plotstatus ); } /* Find nearest x, display that y. */ static void plotstatus_series_update( GtkWidget *widget, Plot *plot, int column, double x, double y ) { double *xcolumn = plot->xcolumn[column]; double *ycolumn = plot->ycolumn[column]; int i; int best; gdouble best_score; best = 0; best_score = IM_ABS( x - xcolumn[0] ); for( i = 1; i < plot->rows; i++ ) { double score = IM_ABS( x - xcolumn[i] ); if( score < best_score ) { best_score = score; best = i; } } set_glabel( widget, "%g", ycolumn[best] ); } void plotstatus_mouse( Plotstatus *plotstatus, double x, double y ) { Plotmodel *plotmodel = plotstatus->plotmodel; Plot *plot = plotmodel->plot; int i; set_glabel( plotstatus->pos, "(%05g, %05g)", x, y ); g_assert( plotstatus->columns <= plot->columns ); for( i = 0; i < plotstatus->columns; i++ ) plotstatus_series_update( plotstatus->label[i], plot, i, x, y ); } ================================================ FILE: src/plotstatus.h ================================================ /* display plot info and mouse posn */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PLOTSTATUS (plotstatus_get_type()) #define PLOTSTATUS( obj ) (GTK_CHECK_CAST( (obj), TYPE_PLOTSTATUS, Plotstatus )) #define PLOTSTATUS_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PLOTSTATUS, PlotstatusClass )) #define IS_PLOTSTATUS( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PLOTSTATUS )) #define IS_PLOTSTATUS_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PLOTSTATUS )) struct _Plotstatus { GtkFrame parent_class; Plotmodel *plotmodel; GtkWidget *top; /* Top label */ GtkWidget *pos; /* Position */ GtkWidget *hb; /* Band element hbox */ GtkWidget *mag; /* Magnification display */ GtkWidget **label; /* A label for displaying each series */ int columns; /* Last number of columns we saw */ }; typedef struct _PlotstatusClass { GtkFrameClass parent_class; /* My methods. */ } PlotstatusClass; GtkType plotstatus_get_type( void ); Plotstatus *plotstatus_new( Plotmodel *plotmodel ); void plotstatus_mouse( Plotstatus *plotstatus, double x, double y ); ================================================ FILE: src/plotview.c ================================================ /* run the display for a plotview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_GEO #define DEBUG */ #include "ip.h" #ifdef HAVE_LIBGOFFICE static GraphicviewClass *parent_class = NULL; static void plotview_destroy( GtkObject *object ) { Plotview *plotview; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PLOTVIEW( object ) ); #ifdef DEBUG printf( "plotview_destroy\n" ); #endif /*DEBUG*/ plotview = PLOTVIEW( object ); GOG_UNREF( plotview->gplot ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void plotview_refresh( vObject *vobject ) { Plotview *plotview = PLOTVIEW( vobject ); Plot *plot = PLOT( VOBJECT( plotview )->iobject ); #ifdef DEBUG printf( "plotview_refresh\n" ); #endif /*DEBUG*/ /* Can't refresh before model build. */ if( plot->rows == 0 || plot->columns == 0 ) return; set_gcaption( plotview->label, "%s", NN( IOBJECT( plot )->caption ) ); GOG_UNREF( plotview->gplot ); plotview->gplot = plot_new_gplot( plot ); gog_object_add_by_name( GOG_OBJECT( plotview->gchart ), "Plot", GOG_OBJECT( plotview->gplot ) ); plot_style_thumbnail( plot, plotview->gchart ); gtk_widget_show_all( plotview->canvas ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void plotview_link( View *view, Model *model, View *parent ) { Plotview *plotview = PLOTVIEW( view ); Rowview *rview = ROWVIEW( parent->parent ); VIEW_CLASS( parent_class )->link( view, model, parent ); rowview_menu_attach( rview, GTK_WIDGET( plotview->box ) ); } static void plotview_class_init( PlotviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = plotview_destroy; vobject_class->refresh = plotview_refresh; view_class->link = plotview_link; } static void plotview_tooltip_generate( GtkWidget *widget, VipsBuf *buf, Plotview *plotview ) { Plot *plot = PLOT( VOBJECT( plotview )->iobject ); IMAGE *im; vips_buf_rewind( buf ); vips_buf_appends( buf, vips_buf_all( &plot->caption_buffer ) ); vips_buf_appendf( buf, ", %s, %s", plot_f2c( plot->format ), plot_s2c( plot->style ) ); if( (im = imageinfo_get( FALSE, plot->value.ii )) ) { vips_buf_appends( buf, ", " ); vips_buf_appendi( buf, im ); } } static void plotview_doubleclick_one_cb( GtkWidget *widget, GdkEvent *event, Plotview *plotview ) { Heapmodel *heapmodel = HEAPMODEL( VOBJECT( plotview )->iobject ); Row *row = heapmodel->row; row_select_modifier( row, event->button.state ); } static void plotview_doubleclick_two_cb( GtkWidget *widget, GdkEvent *event, Plotview *plotview ) { Plot *plot = PLOT( VOBJECT( plotview )->iobject ); model_edit( widget, MODEL( plot ) ); } static void plotview_init( Plotview *plotview ) { GtkWidget *eb; #ifdef DEBUG printf( "plotview_init\n" ); #endif /*DEBUG*/ eb = gtk_event_box_new(); gtk_box_pack_start( GTK_BOX( plotview ), eb, FALSE, FALSE, 0 ); gtk_widget_show( eb ); gtk_widget_set_name( eb, "caption_widget" ); set_tooltip_generate( eb, (TooltipGenerateFn) plotview_tooltip_generate, plotview, NULL ); doubleclick_add( eb, FALSE, DOUBLECLICK_FUNC( plotview_doubleclick_one_cb ), plotview, DOUBLECLICK_FUNC( plotview_doubleclick_two_cb ), plotview ); plotview->box = gtk_vbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( eb ), plotview->box ); gtk_widget_show( plotview->box ); plotview->canvas = go_graph_widget_new( NULL ); gtk_box_pack_start( GTK_BOX( plotview->box ), plotview->canvas, FALSE, FALSE, 0 ); plotview->gchart = go_graph_widget_get_chart( GO_GRAPH_WIDGET( plotview->canvas ) ); gtk_widget_set_size_request( GTK_WIDGET( plotview->canvas ), DISPLAY_THUMBNAIL, DISPLAY_THUMBNAIL ); plotview->gplot = NULL; plotview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( plotview->label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( plotview->label ), 2, 0 ); gtk_box_pack_end( GTK_BOX( plotview->box ), GTK_WIDGET( plotview->label ), FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( plotview->label ) ); } GtkType plotview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Plotview", sizeof( Plotview ), sizeof( PlotviewClass ), (GtkClassInitFunc) plotview_class_init, (GtkObjectInitFunc) plotview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( type ); } View * plotview_new( void ) { Plotview *plotview = gtk_type_new( TYPE_PLOTVIEW ); return( VIEW( plotview ) ); } #endif /*HAVE_LIBGOFFICE*/ ================================================ FILE: src/plotview.h ================================================ /* a plotview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PLOTVIEW (plotview_get_type()) #define PLOTVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_PLOTVIEW, Plotview )) #define PLOTVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PLOTVIEW, PlotviewClass )) #define IS_PLOTVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PLOTVIEW )) #define IS_PLOTVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PLOTVIEW )) typedef struct _Plotview { Graphicview parent_object; GtkWidget *box; GtkWidget *label; GtkWidget *canvas; GogChart *gchart; GogPlot *gplot; } Plotview; typedef struct _PlotviewClass { GraphicviewClass parent_class; /* My methods. */ } PlotviewClass; GtkType plotview_get_type( void ); View *plotview_new( void ); ================================================ FILE: src/plotwindow.c ================================================ /* a plotpresent / plotmodel in a floating window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static FloatwindowClass *parent_class = NULL; static void plotwindow_destroy( GtkObject *object ) { Plotwindow *plotwindow; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PLOTWINDOW( object ) ); plotwindow = PLOTWINDOW( object ); #ifdef DEBUG printf( "plotwindow_destroy: %p\n", plotwindow ); #endif /*DEBUG*/ /* My instance destroy stuff. */ UNREF( plotwindow->plotmodel ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void plotwindow_class_init( PlotwindowClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = plotwindow_destroy; /* Create signals. */ /* Init methods. */ } static void plotwindow_init( Plotwindow *plotwindow ) { #ifdef DEBUG printf( "plotwindow_init: %p\n", plotwindow ); #endif /*DEBUG*/ plotwindow->plotmodel = NULL; } GtkType plotwindow_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Plotwindow", sizeof( Plotwindow ), sizeof( PlotwindowClass ), (GtkClassInitFunc) plotwindow_class_init, (GtkObjectInitFunc) plotwindow_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_FLOATWINDOW, &info ); } return( type ); } static void plotwindow_refresh_title( Plotwindow *plotwindow ) { Plotmodel *plotmodel = plotwindow->plotmodel; Plot *plot = plotmodel->plot; Row *row = HEAPMODEL( plot )->row; Workspace *ws = row_get_workspace( row ); #ifdef DEBUG printf( "plotwindow_refresh_title\n" ); #endif /*DEBUG*/ /* Can come here during ws destroy. */ if( ws ) { VipsBuf buf; char txt[512]; vips_buf_init_static( &buf, txt, 512 ); row_qualified_name_relative( ws->sym, row, &buf ); iwindow_set_title( IWINDOW( plotwindow ), "%s", vips_buf_all( &buf ) ); } } /* The model has changed ... update our menus and titlebar. */ static void plotwindow_changed_cb( Plotmodel *plotmodel, Plotwindow *plotwindow ) { iWindow *iwnd = IWINDOW( plotwindow ); GtkAction *action; plotwindow_refresh_title( plotwindow ); action = gtk_action_group_get_action( iwnd->action_group, "Status" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), plotmodel->show_status ); } static void plotwindow_mouse_move_cb( Plotpresent *plotpresent, double x, double y, Plotwindow *plotwindow ) { plotstatus_mouse( plotwindow->plotstatus, x, y ); } static void plotwindow_show_status_action_cb( GtkToggleAction *action, Plotwindow *plotwindow ) { plotmodel_set_status( plotwindow->plotmodel, gtk_toggle_action_get_active( action ) ); } static void plotwindow_export_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { #ifdef HAVE_LIBGOFFICE #ifdef HAVE_LIBGSF Filesel *filesel = FILESEL( iwnd ); Plotwindow *plotwindow = (Plotwindow *) client; Plotpresent *plotpresent = plotwindow->plotpresent; GogGraph *ggraph = plotpresent->ggraph; char *filename; char buf[FILENAME_MAX]; char *extension; GOImageFormat format; GsfOutput *output; GError *err = NULL; gboolean result; if( !(filename = filesel_get_filename( filesel )) ) { nfn( sys, IWINDOW_ERROR ); return; } expand_variables( filename, buf ); if( !(output = gsf_output_stdio_new( buf, &err )) ) { error_top( _( "Unable to write." ) ); if( err ) error_sub( "%s", err->message ); IM_FREEF( g_error_free, err ); g_free( filename ); nfn( sys, IWINDOW_ERROR ); return; } if( (extension = strrchr( buf, '.' )) ) extension += 1; else extension = buf; format = go_image_get_format_from_name( extension ); g_free( filename ); result = gog_graph_export_image( ggraph, format, output, 72, 72 ); UNREF( output ); nfn( sys, result ? IWINDOW_YES : IWINDOW_ERROR ); #endif /*HAVE_LIBGSF*/ #endif /*HAVE_LIBGOFFICE*/ } static void plotwindow_export_action_cb( GtkAction *action, Plotwindow *plotwindow ) { Filesel *filesel = FILESEL( filesel_new() ); iwindow_set_title( IWINDOW( filesel ), "%s", _( "Export Plot As" ) ); filesel_set_flags( filesel, TRUE, TRUE ); filesel_set_filetype( filesel, filesel_type_image, IMAGE_FILE_TYPE ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( plotwindow ) ); filesel_set_done( filesel, plotwindow_export_done_cb, plotwindow ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } static GtkToggleActionEntry plotwindow_toggle_actions[] = { { "Status", NULL, N_( "_Status" ), NULL, N_( "Show status bar" ), G_CALLBACK( plotwindow_show_status_action_cb ), TRUE } }; static GtkActionEntry plotwindow_actions[] = { { "Export", GTK_STOCK_SAVE_AS, N_( "Export Plot" ), NULL, N_( "Export plot to file" ), G_CALLBACK( plotwindow_export_action_cb ) } }; static const char *plotwindow_menubar_ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static void plotwindow_build( Plotwindow *plotwindow, GtkWidget *vbox, Plot *plot ) { iWindow *iwnd = IWINDOW( plotwindow ); GError *error; GtkWidget *mbar; GtkWidget *frame; GList *focus_chain; int w, h; /* Make our model. */ plotwindow->plotmodel = plotmodel_new( plot ); g_object_ref( G_OBJECT( plotwindow->plotmodel ) ); iobject_sink( IOBJECT( plotwindow->plotmodel ) ); g_signal_connect( G_OBJECT( plotwindow->plotmodel ), "changed", G_CALLBACK( plotwindow_changed_cb ), plotwindow ); /* Make main menu bar */ gtk_action_group_add_actions( iwnd->action_group, plotwindow_actions, G_N_ELEMENTS( plotwindow_actions ), GTK_WINDOW( plotwindow ) ); gtk_action_group_add_toggle_actions( iwnd->action_group, plotwindow_toggle_actions, G_N_ELEMENTS( plotwindow_toggle_actions ), GTK_WINDOW( plotwindow ) ); error = NULL; if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager, plotwindow_menubar_ui_description, -1, &error ) ) { g_message( "building menus failed: %s", error->message ); g_error_free( error ); exit( EXIT_FAILURE ); } mbar = gtk_ui_manager_get_widget( iwnd->ui_manager, "/PlotwindowMenubar" ); gtk_box_pack_start( GTK_BOX( vbox ), mbar, FALSE, FALSE, 0 ); gtk_widget_show( mbar ); /* Status bar. Show/hide set on first refresh. */ plotwindow->plotstatus = plotstatus_new( plotwindow->plotmodel ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( plotwindow->plotstatus ), FALSE, FALSE, 0 ); /* Plot area. */ frame = gtk_frame_new( NULL ); gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_OUT ); gtk_widget_show( frame ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( frame ), TRUE, TRUE, 0 ); #ifdef HAVE_LIBGOFFICE plotwindow->plotpresent = plotpresent_new( plotwindow->plotmodel ); #endif /*HAVE_LIBGOFFICE*/ gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( plotwindow->plotpresent ) ); gtk_widget_show( GTK_WIDGET( plotwindow->plotpresent ) ); g_signal_connect( G_OBJECT( plotwindow->plotpresent ), "mouse_move", G_CALLBACK( plotwindow_mouse_move_cb ), plotwindow ); /* Initial window size. */ if( MODEL( plot )->window_width == -1 ) { w = IM_MIN( IMAGE_WINDOW_WIDTH, 500 ); h = IM_MIN( IMAGE_WINDOW_HEIGHT, 500 ); gtk_window_set_default_size( GTK_WINDOW( plotwindow ), w, h ); } /* Override the focus_chain ... we want the imagedisplay first. */ focus_chain = NULL; focus_chain = g_list_append( focus_chain, plotwindow->plotpresent ); gtk_container_set_focus_chain( GTK_CONTAINER( vbox ), focus_chain ); gtk_widget_grab_focus( GTK_WIDGET( plotwindow->plotpresent ) ); } static void plotwindow_popdown( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Plotwindow *plotwindow = PLOTWINDOW( iwnd ); Plotmodel *plotmodel = plotwindow->plotmodel; Plot *plot = plotmodel->plot; /* We have to note position/size in popdown rather than destroy, since * the widgets have to all still be extant. */ plot->show_status = plotmodel->show_status; nfn( sys, IWINDOW_YES ); } static void plotwindow_link( Plotwindow *plotwindow, Plot *plot, GtkWidget *parent ) { iwindow_set_build( IWINDOW( plotwindow ), (iWindowBuildFn) plotwindow_build, plot, NULL, NULL ); iwindow_set_parent( IWINDOW( plotwindow ), parent ); iwindow_set_popdown( IWINDOW( plotwindow ), plotwindow_popdown, NULL ); floatwindow_link( FLOATWINDOW( plotwindow ), MODEL( plot ) ); iwindow_build( IWINDOW( plotwindow ) ); /* Initial "changed" on the model to get all views to init. */ iobject_changed( IOBJECT( plotwindow->plotmodel ) ); } Plotwindow * plotwindow_new( Plot *plot, GtkWidget *parent ) { Plotwindow *plotwindow = gtk_type_new( TYPE_PLOTWINDOW ); plotwindow_link( plotwindow, plot, parent ); return( plotwindow ); } ================================================ FILE: src/plotwindow.h ================================================ /* a plotpresent in a floating window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PLOTWINDOW (plotwindow_get_type()) #define PLOTWINDOW( obj ) (GTK_CHECK_CAST( (obj), TYPE_PLOTWINDOW, Plotwindow )) #define PLOTWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PLOTWINDOW, PlotwindowClass )) #define IS_PLOTWINDOW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PLOTWINDOW )) #define IS_PLOTWINDOW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PLOTWINDOW )) struct _Plotwindow { Floatwindow parent_class; /* The model we watch. */ Plotmodel *plotmodel; /* Widgets. */ Plotstatus *plotstatus; Plotpresent *plotpresent; }; typedef struct _PlotwindowClass { FloatwindowClass parent_class; /* My methods. */ } PlotwindowClass; GtkType plotwindow_get_type( void ); Plotwindow *plotwindow_new( Plot *plot, GtkWidget *parent ); ================================================ FILE: src/popupbutton.c ================================================ /* a button that displays a popup menu * * quick hack from totem-plugin-viewer.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GtkToggleButtonClass *popupbutton_parent_class = NULL; static void popupbutton_class_init( PopupbuttonClass *class ) { popupbutton_parent_class = g_type_class_peek_parent( class ); } static void popupbutton_init( Popupbutton *popupbutton ) { popupbutton->menu = NULL; } GType popupbutton_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PopupbuttonClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) popupbutton_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Popupbutton ), 32, /* n_preallocs */ (GInstanceInitFunc) popupbutton_init, }; type = g_type_register_static( GTK_TYPE_TOGGLE_BUTTON, "Popupbutton", &info, 0 ); } return( type ); } static void popupbutton_position_func( GtkMenu *menu, gint *x, gint *y, gboolean *push_in, GtkWidget *button ) { GtkRequisition menu_req; GtkTextDirection direction; GtkAllocation allocation; gtk_widget_size_request( GTK_WIDGET( menu ), &menu_req ); direction = gtk_widget_get_direction( button ); gdk_window_get_origin( gtk_widget_get_window( button ), x, y ); gtk_widget_get_allocation( button, &allocation ); *x += allocation.x; *y += allocation.y; if( direction == GTK_TEXT_DIR_LTR ) *x += VIPS_MAX( allocation.width - menu_req.width, 0 ); else if( menu_req.width > allocation.width ) *x -= menu_req.width - allocation.width; *y += allocation.height; *push_in = FALSE; } static void popupbutton_over_arrow( Popupbutton *popupbutton, GdkEventButton *event ) { GtkWidget *menu = popupbutton->menu; gtk_menu_popup( GTK_MENU( menu ), NULL, NULL, (GtkMenuPositionFunc) popupbutton_position_func, popupbutton, event ? event->button : 0, event ? event->time : gtk_get_current_event_time() ); } static void popupbutton_toggled_cb( Popupbutton *popupbutton ) { GtkWidget *menu = popupbutton->menu; if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( popupbutton ) ) && !gtk_widget_get_visible( menu ) ) { /* We get here only when the menu is activated by a key * press, so that we can select the first menu item. */ popupbutton_over_arrow( popupbutton, NULL ); gtk_menu_shell_select_first( GTK_MENU_SHELL( menu ), FALSE ); } } static gboolean popupbutton_button_press_event_cb( Popupbutton *popupbutton, GdkEventButton *event ) { if( event->button == 1 ) { GtkWidget *menu = popupbutton->menu; if( !gtk_widget_get_visible( menu ) ) { popupbutton_over_arrow( popupbutton, event ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( popupbutton ), TRUE ); } else { gtk_menu_popdown( GTK_MENU( menu ) ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( popupbutton ), FALSE ); } return TRUE; } return FALSE; } Popupbutton * popupbutton_new( void ) { Popupbutton *popupbutton; GtkWidget *image; popupbutton = g_object_new( TYPE_POPUPBUTTON, NULL ); image = gtk_image_new_from_stock( GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU ); gtk_container_add( GTK_CONTAINER( popupbutton ), image ); gtk_widget_show( image ); g_signal_connect( popupbutton, "toggled", G_CALLBACK( popupbutton_toggled_cb ), NULL ); g_signal_connect( popupbutton, "button-press-event", G_CALLBACK( popupbutton_button_press_event_cb ), NULL ); return( popupbutton ); } static void popupbutton_menu_unmap_cb( GtkWidget *menu, Popupbutton *popupbutton ) { gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( popupbutton ), FALSE ); } void popupbutton_set_menu( Popupbutton *popupbutton, GtkWidget *menu ) { g_assert( !popupbutton->menu ); popupbutton->menu = menu; g_signal_connect( menu, "unmap", G_CALLBACK( popupbutton_menu_unmap_cb ), popupbutton ); } ================================================ FILE: src/popupbutton.h ================================================ /* a button that displays a popup menu * * quick hack from totem-plugin-viewer.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_POPUPBUTTON (popupbutton_get_type()) #define POPUPBUTTON( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_POPUPBUTTON, Popupbutton )) #define POPUPBUTTON_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_POPUPBUTTON, PopupbuttonClass )) #define IS_POPUPBUTTON( obj ) (GTK_CHECK_TYPE( (obj), TYPE_POPUPBUTTON )) #define IS_POPUPBUTTON_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_POPUPBUTTON )) typedef struct _Popupbutton { GtkToggleButton parent_object; GtkWidget *menu; } Popupbutton; typedef struct _PopupbuttonClass { GtkToggleButtonClass parent_class; } PopupbuttonClass; GtkType popupbutton_get_type( void ); Popupbutton *popupbutton_new( void ); void popupbutton_set_menu( Popupbutton *Popupbutton, GtkWidget *menu ); ================================================ FILE: src/predicate.c ================================================ /* Symbol classifiers. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* Is it one of the system members? Hidden in menus and class display. We * can't safely use is_super()/is_this() (they are fast), because we can get * called during build (before they are working). Use strcmp() instead. */ gboolean is_system( Symbol *sym ) { Symbol *parent = symbol_get_parent( sym ); /* Something like $$lambda1 and friends. */ if( sym->generated ) return( TRUE ); if( strcmp( IOBJECT( sym )->name, MEMBER_CHECK ) == 0 || strcmp( IOBJECT( sym )->name, MEMBER_NAME ) == 0 || strcmp( IOBJECT( sym )->name, MEMBER_THIS ) == 0 || IOBJECT( sym )->name[0] == '_' ) return( TRUE ); if( parent && !is_scope( parent ) && strcmp( IOBJECT( sym )->name, IOBJECT( parent )->name ) == 0 ) return( TRUE ); return( FALSE ); } /* Something like "a = Separator;" */ gboolean is_separator( Symbol *sym ) { if( sym->expr && sym->expr->compile && sym->expr->compile->tree && sym->expr->compile->tree->type == NODE_LEAF ) { Symbol *leaf = sym->expr->compile->tree->leaf; return( strcmp( IOBJECT( leaf )->name, CLASS_SEPARATOR ) == 0 ); } return( FALSE ); } /* Is a symbol a class. */ gboolean is_class( Compile *compile ) { return( compile->is_klass ); } /* Is a sym the super member of some class. */ gboolean is_super( Symbol *sym ) { Symbol *parent = symbol_get_parent( sym ); Compile *parent_compile = parent->expr->compile; return( parent_compile && is_class( parent_compile ) && sym == parent_compile->super ); } /* Is a sym the this member of some class. */ gboolean is_this( Symbol *sym ) { Symbol *parent = symbol_get_parent( sym ); Compile *parent_compile = parent->expr->compile; return( is_class( parent_compile ) && sym == parent_compile->this ); } /* Is sym a member of an enclosing class of compile. */ gboolean is_member_enclosing( Compile *compile, Symbol *sym ) { for( compile = compile_get_parent( compile ); compile; compile = compile_get_parent( compile ) ) if( is_class( compile ) && compile->sym != sym && ICONTAINER( sym )->parent == ICONTAINER( compile ) ) return( TRUE ); return( FALSE ); } /* Is a symbol a compile-time scope (eg. workspace) */ gboolean is_scope( Symbol *sym ) { return( sym->type == SYM_ROOT || sym->type == SYM_WORKSPACE || sym->type == SYM_WORKSPACEROOT || !symbol_get_parent( sym ) ); } /* Is a symbol a top-level definition. Tops are symbols whose parents are * SYM_ROOT, SYM_WORKSPACE and friends. */ gboolean is_top( Symbol *sym ) { if( is_scope( sym ) || is_scope( symbol_get_parent( sym ) ) ) return( TRUE ); return( FALSE ); } /* Is a symbol a member of a class? Params don't count. */ gboolean is_member( Symbol *sym ) { return( is_value( sym ) && is_class( COMPILE( ICONTAINER( sym )->parent ) ) ); } /* Is a compile a member function (not a sub-class)? */ gboolean is_memberfunc( Compile *compile ) { return( is_class( compile_get_parent( compile ) ) && !is_class( compile ) ); } /* Something that ought to have a value. */ gboolean is_value( Symbol *sym ) { return( sym->type == SYM_VALUE && sym->expr ); } /* Is sym an ancestor of context? */ gboolean is_ancestor( Symbol *context, Symbol *sym ) { if( context == sym ) return( TRUE ); if( context == symbol_root ) return( FALSE ); return( is_ancestor( symbol_get_parent( context ), sym ) ); } gboolean is_menuable( Symbol *sym ) { /* In a hidden kit? */ if( sym->tool && IOBJECT( sym->tool->kit )->name[0] == '_' ) return( FALSE ); /* A hidden item? */ if( IOBJECT( sym )->name[0] == '_' ) return( FALSE ); /* We also hide all supers, system things */ if( !is_value( sym ) || !sym->expr->compile || is_system( sym ) || strcmp( IOBJECT( sym )->name, MEMBER_SUPER ) == 0 ) return( FALSE ); return( TRUE ); } ================================================ FILE: src/predicate.h ================================================ /* Declarations for predicate.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ gboolean is_system( Symbol *sym ); gboolean is_separator( Symbol *sym ); gboolean is_member( Symbol *sym ); gboolean is_class( Compile *compile ); gboolean is_super( Symbol *sym ); gboolean is_this( Symbol *sym ); gboolean is_member_enclosing( Compile *compile, Symbol *sym ); gboolean is_top( Symbol *sym ); gboolean is_scope( Symbol *sym ); gboolean is_memberfunc( Compile *compile ); gboolean is_value( Symbol *sym ); gboolean is_ancestor( Symbol *context, Symbol *sym ); gboolean is_menuable( Symbol *sym ); ================================================ FILE: src/prefcolumnview.c ================================================ /* a view of a column */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static void prefcolumnview_refresh( vObject *vobject ) { Prefcolumnview *pcview = PREFCOLUMNVIEW( vobject ); Column *col = COLUMN( VOBJECT( pcview )->iobject ); char buf[256]; char buf2[256]; escape_markup( IOBJECT( col )->caption, buf2, 256 ); im_snprintf( buf, 256, "%s", buf2 ); gtk_label_set_markup( GTK_LABEL( pcview->lab ), buf ); /* Closed columns are hidden. */ widget_visible( GTK_WIDGET( pcview ), col->open ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void prefcolumnview_child_add( View *parent, View *child ) { Prefcolumnview *pcview = PREFCOLUMNVIEW( parent ); Subcolumnview *sview = SUBCOLUMNVIEW( child ); VIEW_CLASS( parent_class )->child_add( parent, child ); gtk_box_pack_end( GTK_BOX( pcview ), GTK_WIDGET( sview ), FALSE, FALSE, 0 ); } static void prefcolumnview_class_init( PrefcolumnviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = prefcolumnview_refresh; view_class->child_add = prefcolumnview_child_add; } static void prefcolumnview_init( Prefcolumnview *pcview ) { pcview->lab = gtk_label_new( "" ); gtk_box_pack_start( GTK_BOX( pcview ), pcview->lab, FALSE, FALSE, 2 ); gtk_misc_set_padding( GTK_MISC( pcview->lab ), 2, 6 ); gtk_misc_set_alignment( GTK_MISC( pcview->lab ), 0, 0.5 ); gtk_widget_show_all( GTK_WIDGET( pcview ) ); } GtkType prefcolumnview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Prefcolumnview", sizeof( Prefcolumnview ), sizeof( PrefcolumnviewClass ), (GtkClassInitFunc) prefcolumnview_class_init, (GtkObjectInitFunc) prefcolumnview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VIEW, &info ); } return( type ); } View * prefcolumnview_new( void ) { Prefcolumnview *pcview = gtk_type_new( TYPE_PREFCOLUMNVIEW ); return( VIEW( pcview ) ); } ================================================ FILE: src/prefcolumnview.h ================================================ /* view of a column in a preferences window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PREFCOLUMNVIEW (prefcolumnview_get_type()) #define PREFCOLUMNVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PREFCOLUMNVIEW, Prefcolumnview )) #define PREFCOLUMNVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_PREFCOLUMNVIEW, PrefcolumnviewClass )) #define IS_PREFCOLUMNVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PREFCOLUMNVIEW )) #define IS_PREFCOLUMNVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PREFCOLUMNVIEW )) struct _Prefcolumnview { View view; /* Display parts. */ GtkWidget *lab; /* Prefcolumnview name label */ }; typedef struct _PrefcolumnviewClass { ViewClass parent_class; /* My methods. */ } PrefcolumnviewClass; GtkType prefcolumnview_get_type( void ); View *prefcolumnview_new( void ); ================================================ FILE: src/prefs.c ================================================ /* preferences dialog */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static iDialogClass *parent_class = NULL; static void prefs_destroy( GtkObject *object ) { Prefs *prefs = PREFS( object ); #ifdef DEBUG printf( "prefs_destroy\n" ); #endif /*DEBUG*/ if( prefs->ws ) { Workspacegroup *wsg = workspace_get_workspacegroup( prefs->ws ); Filemodel *filemodel = FILEMODEL( wsg ); /* Force a recalc, in case we've changed the autorecalc * settings. Also does a scan on any widgets. */ symbol_recalculate_all_force( TRUE ); if( filemodel->modified && filemodel_top_save( filemodel, filemodel->filename ) ) filemodel_set_modified( filemodel, FALSE ); } /* My instance destroy stuff. */ FREESID( prefs->destroy_sid, prefs->ws ); IM_FREE( prefs->caption_filter ); prefs->ws = NULL; GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void prefs_build( GtkWidget *widget ) { Prefs *prefs = PREFS( widget ); GtkWidget *work; #ifdef DEBUG printf( "prefs_build: %p\n", prefs ); #endif /*DEBUG*/ /* Call all builds in superclasses. */ IWINDOW_CLASS( parent_class )->build( widget ); work = IDIALOG( prefs )->work; prefs->pwview = PREFWORKSPACEVIEW( prefworkspaceview_new() ); prefworkspaceview_set_caption_filter( prefs->pwview, prefs->caption_filter ); view_link( VIEW( prefs->pwview ), MODEL( prefs->ws ), NULL ); if( prefs->caption_filter ) { gtk_box_pack_start( GTK_BOX( work ), GTK_WIDGET( prefs->pwview ), TRUE, TRUE, 0 ); gtk_widget_show( GTK_WIDGET( prefs->pwview ) ); } else { /* No caption_filter set, so this is probably a big prefs * window. Build a scrolledwindow for the content. */ GtkWidget *window; window = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( window ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( window ), GTK_WIDGET( prefs->pwview ) ); gtk_viewport_set_shadow_type( GTK_VIEWPORT( GTK_BIN( window )->child ), GTK_SHADOW_NONE ); gtk_box_pack_start( GTK_BOX( work ), GTK_WIDGET( window ), TRUE, TRUE, 0 ); gtk_widget_show( GTK_WIDGET( prefs->pwview ) ); gtk_widget_show( window ); } } static void prefs_class_init( PrefsClass *class ) { GtkObjectClass *gobject_class = (GtkObjectClass *) class; iWindowClass *iwindow_class = (iWindowClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->destroy = prefs_destroy; iwindow_class->build = prefs_build; /* Create signals. */ /* Init methods. */ } static void prefs_init( Prefs *prefs ) { prefs->ws = NULL; prefs->destroy_sid = 0; } GType prefs_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( PrefsClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) prefs_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Prefs ), 32, /* n_preallocs */ (GInstanceInitFunc) prefs_init, }; type = g_type_register_static( TYPE_IDIALOG, "Prefs", &info, 0 ); } return( type ); } static void prefs_workspace_destroy_cb( Workspace *ws, Prefs *prefs ) { prefs->destroy_sid = 0; prefs->ws = NULL; iwindow_kill( IWINDOW( prefs ) ); } static void prefs_link( Prefs *prefs, Workspace *ws ) { g_assert( !prefs->ws ); prefs->ws = ws; prefs->ws->mode = WORKSPACE_MODE_NOEDIT; prefs->destroy_sid = g_signal_connect( ws, "destroy", G_CALLBACK( prefs_workspace_destroy_cb ), prefs ); } static gint prefs_column_compare( Column *a, Column *b ) { return( b->y - a->y ); } Prefs * prefs_new( const char *caption_filter ) { Symbol *wsr_sym = main_workspaceroot->sym; Symbol *ws_sym = SYMBOL( icontainer_child_lookup( ICONTAINER( wsr_sym->expr->compile ), "Preferences" ) ); Prefs *prefs; if( !ws_sym ) { /* Probably failed to load prefs on startup for some reason. */ error_top( _( "Unable to display preferences." ) ); error_sub( _( "No preferences workspace was found. " "Preferences probably failed to load when " "%s started." ), PACKAGE ); return( NULL ); } icontainer_custom_sort( ICONTAINER( ws_sym->ws ), (GCompareFunc) prefs_column_compare ); prefs = PREFS( g_object_new( TYPE_PREFS, NULL ) ); IM_SETSTR( prefs->caption_filter, caption_filter ); prefs_link( prefs, ws_sym->ws ); return( prefs ); } gboolean prefs_set( const char *name, const char *fmt, ... ) { Watch *watch; if( main_watchgroup && (watch = watch_find( main_watchgroup, name )) ) { va_list args; va_start( args, fmt ); watch_vset( watch, fmt, args ); va_end( args ); } return( TRUE ); } ================================================ FILE: src/prefs.h ================================================ /* Declarations for the preferences dialog. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PREFS (prefs_get_type()) #define PREFS( obj ) (GTK_CHECK_CAST( (obj), TYPE_PREFS, Prefs )) #define PREFS_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PREFS, PrefsClass )) #define IS_PREFS( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PREFS )) #define IS_PREFS_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PREFS )) typedef struct _Prefs { iDialog parent_object; /* Workspace we display. */ Workspace *ws; guint destroy_sid; Prefworkspaceview *pwview; /* (optionally) filter prefs with this. */ char *caption_filter; } Prefs; typedef struct _PrefsClass { iWindowClass parent_class; /* My methods. */ } PrefsClass; GType prefs_get_type( void ); Prefs *prefs_new( const char *caption_filter ); gboolean prefs_set( const char *name, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); ================================================ FILE: src/prefworkspaceview.c ================================================ /* a prefworkspaceview button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Define to trace button press events. #define EVENT */ #include "ip.h" static ViewClass *parent_class = NULL; static void prefworkspaceview_destroy( GtkObject *object ) { Prefworkspaceview *pwview; #ifdef DEBUG printf( "prefworkspaceview_destroy\n" ); #endif /*DEBUG*/ g_return_if_fail( object != NULL ); g_return_if_fail( IS_PREFWORKSPACEVIEW( object ) ); pwview = PREFWORKSPACEVIEW( object ); /* Instance destroy. */ IM_FREE( pwview->caption_filter ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void prefworkspaceview_child_add( View *parent, View *child ) { Prefworkspaceview *pwview = PREFWORKSPACEVIEW( parent ); VIEW_CLASS( parent_class )->child_add( parent, child ); gtk_box_pack_end( GTK_BOX( pwview ), GTK_WIDGET( child ), FALSE, FALSE, 0 ); } /* Should a child model have a display? */ static gboolean prefworkspaceview_display( View *parent, Model *child ) { Prefworkspaceview *pwview = PREFWORKSPACEVIEW( parent ); Column *column = COLUMN( child ); if( pwview->caption_filter ) return( strstr( IOBJECT( column )->caption, pwview->caption_filter ) != NULL ); else return( TRUE ); } static void prefworkspaceview_class_init( PrefworkspaceviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = prefworkspaceview_destroy; view_class->child_add = prefworkspaceview_child_add; view_class->display = prefworkspaceview_display; } static void prefworkspaceview_init( Prefworkspaceview *pwview ) { pwview->caption_filter = NULL; } GtkType prefworkspaceview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Prefworkspaceview", sizeof( Prefworkspaceview ), sizeof( PrefworkspaceviewClass ), (GtkClassInitFunc) prefworkspaceview_class_init, (GtkObjectInitFunc) prefworkspaceview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VIEW, &info ); } return( type ); } View * prefworkspaceview_new( void ) { Prefworkspaceview *pwview = gtk_type_new( TYPE_PREFWORKSPACEVIEW ); return( VIEW( pwview ) ); } void prefworkspaceview_set_caption_filter( Prefworkspaceview *pwview, const char *caption_filter ) { IM_SETSTR( pwview->caption_filter, caption_filter ); /* caption_filter is a property of the view, not the model, so we have * to queue a refresh rather than just signalling change. */ vobject_refresh_queue( VOBJECT( pwview ) ); } ================================================ FILE: src/prefworkspaceview.h ================================================ /* a view of a workspace for the preferences window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PREFWORKSPACEVIEW (prefworkspaceview_get_type()) #define PREFWORKSPACEVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PREFWORKSPACEVIEW, Prefworkspaceview )) #define PREFWORKSPACEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_PREFWORKSPACEVIEW, PrefworkspaceviewClass )) #define IS_PREFWORKSPACEVIEW( obj ) \ (GTK_CHECK_TYPE( (obj), TYPE_PREFWORKSPACEVIEW )) #define IS_PREFWORKSPACEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PREFWORKSPACEVIEW )) struct _Prefworkspaceview { View view; /* If set, only display the columns whose caption includes this string * (eg. "JPEG"). Used to display tiny prefs windows for jpeg save etc. */ char *caption_filter; }; typedef struct _PrefworkspaceviewClass { ViewClass parent_class; /* My methods. */ } PrefworkspaceviewClass; GtkType prefworkspaceview_get_type( void ); View *prefworkspaceview_new( void ); void prefworkspaceview_set_caption_filter( Prefworkspaceview *pwview, const char *caption_filter ); ================================================ FILE: src/preview.c ================================================ /* thumbnail widget */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* Number of columns of pixmaps we display. */ #define NUM_COLUMNS (4) static ImagedisplayClass *parent_class = NULL; static void preview_destroy( GtkObject *object ) { Preview *preview; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PREVIEW( object ) ); preview = PREVIEW( object ); UNREF( preview->conv ); IM_FREE( preview->filename ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void preview_class_init( PreviewClass *class ) { GtkObjectClass *object_class; object_class = (GtkObjectClass *) class; object_class->destroy = preview_destroy; parent_class = g_type_class_peek_parent( class ); } static void preview_init( Preview *preview ) { #ifdef DEBUG printf( "preview_init: %p\n", preview ); #endif /*DEBUG*/ preview->filename = NULL; preview->conv = conversion_new( NULL ); preview->conv->tile_size = 16; gtk_widget_set_size_request( GTK_WIDGET( preview ), 128, 128 ); imagedisplay_set_conversion( IMAGEDISPLAY( preview ), preview->conv ); imagedisplay_set_shrink_to_fit( IMAGEDISPLAY( preview ), TRUE ); g_object_ref( G_OBJECT( preview->conv ) ); } GtkType preview_get_type( void ) { static GtkType type = 0; if( !type) { static const GtkTypeInfo info = { "Preview", sizeof( Preview ), sizeof( PreviewClass ), (GtkClassInitFunc) preview_class_init, (GtkObjectInitFunc) preview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_IMAGEDISPLAY, &info ); } return( type ); } Preview * preview_new( void ) { Preview *preview = (Preview *) gtk_type_new( TYPE_PREVIEW ); return( preview ); } static void preview_set_filename_idle( Preview *preview, char *filename ) { Imageinfo *ii; /* Make sure our enclosing preview wasn't been killed before this idle * starts. */ if( !preview->conv ) return; /* This is the call that can take ages and kill everything. */ if( !(ii = imageinfo_new_input( main_imageinfogroup, GTK_WIDGET( preview ), NULL, filename )) ) return; /* So test for alive-ness again. */ if( preview->conv ) { char txt[MAX_LINELENGTH]; VipsBuf buf = VIPS_BUF_STATIC( txt ); conversion_set_image( preview->conv, ii ); IM_SETSTR( preview->filename, filename ); /* How strange, we need this to get the * background to clear fully. */ gtk_widget_queue_draw( GTK_WIDGET( preview ) ); get_image_info( &buf, IOBJECT( preview->conv->ii )->name ); set_tooltip( GTK_WIDGET( preview ), "%s", vips_buf_all( &buf ) ); } MANAGED_UNREF( ii ); } typedef struct _UpdateProxy { Preview *preview; char *filename; } UpdateProxy; static gboolean preview_set_filename_idle_cb( UpdateProxy *proxy ) { preview_set_filename_idle( proxy->preview, proxy->filename ); UNREF( proxy->preview ); g_free( proxy ); /* Don't run again. */ return( FALSE ); } /* We can't load in-line, it can take ages and trigger progress callbacks, * which in turn, could kill our enclosing widget. * * Instead, we do the load in a idle callback and update the preview at the * end, if it's still valid. */ void preview_set_filename( Preview *preview, char *filename ) { UpdateProxy *proxy = g_new( UpdateProxy, 1 ); /* We are going to put the preview into the idle queue. It must remain * valid until the idle handler is handled, so we ref. */ g_object_ref( preview ); proxy->preview = preview; proxy->filename = g_strdup( filename ); g_idle_add( (GSourceFunc) preview_set_filename_idle_cb, proxy ); } ================================================ FILE: src/preview.h ================================================ /* thumbnail widget */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PREVIEW (preview_get_type()) #define PREVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_PREVIEW, Preview )) #define PREVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PREVIEW, PreviewClass )) #define IS_PREVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PREVIEW )) #define IS_PREVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PREVIEW )) struct _Preview { Imagedisplay parent; char *filename; /* The file we are trying to display */ Conversion *conv; /* Hold a ref to the convert object */ }; typedef struct _PreviewClass { ImagedisplayClass parent_class; /* My methods. */ } PreviewClass; GtkType preview_get_type( void ); Preview *preview_new( void ); void preview_set_filename( Preview *preview, char *filename ); ================================================ FILE: src/program.c ================================================ /* program window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG #define DEBUG_TREE */ #include "ip.h" /* Keep tools/kits in a treestore. Also pointers to managed objects. */ enum { NAME_COLUMN, /* Kit or tool name */ TOOL_POINTER_COLUMN, /* Pointer to tool */ KIT_POINTER_COLUMN, /* Pointer to kit (if no tool) */ N_COLUMNS }; static iWindowClass *parent_class = NULL; static GSList *program_all = NULL; static GtkWidget *program_menu = NULL; static Model * program_get_selected( Program *program ) { Model *model; if( program->tool ) model = MODEL( program->tool ); else if( program->kit ) model = MODEL( program->kit ); else model = NULL; return( model ); } static void program_info( Program *program, VipsBuf *buf ) { Model *model = program_get_selected( program ); vips_buf_appendf( buf, _( "Edit window" ) ); vips_buf_appendf( buf, "\n" ); vips_buf_appendf( buf, "dirty = \"%s\"\n", bool_to_char( program->dirty ) ); vips_buf_appendf( buf, "\n" ); if( model ) { iobject_info( IOBJECT( model ), buf ); vips_buf_appendf( buf, "\n" ); } } gboolean my_strcmp( const char *a, const char *b ) { if( a == b ) return( 0 ); if( !a ) return( -1 ); if( !b ) return( -1 ); return( strcmp( a, b ) ); } /* Remove this and any subsequent nodes at this level. */ static void program_refresh_trim( Program *program, GtkTreePath *path ) { GtkTreeIter iter; while( gtk_tree_model_get_iter( GTK_TREE_MODEL( program->store ), &iter, path ) ) { #ifdef DEBUG_TREE printf( "program_refresh_trim: removing %s\n", gtk_tree_path_to_string ( path ) ); #endif /*DEBUG_TREE*/ gtk_tree_store_remove( program->store, &iter ); } } static void program_refresh_update( Program *program, GtkTreePath *path, const char *name, Tool *tool, Toolkit *kit ) { GtkTreeIter iter; /* Update, or append if there's nothing to update. */ if( gtk_tree_model_get_iter( GTK_TREE_MODEL( program->store ), &iter, path ) ) { /* Node exists. */ char *store_name; Tool *store_tool; Toolkit *store_kit; gtk_tree_model_get( GTK_TREE_MODEL( program->store ), &iter, NAME_COLUMN, &store_name, TOOL_POINTER_COLUMN, &store_tool, KIT_POINTER_COLUMN, &store_kit, -1 ); if( tool != store_tool || kit != store_kit || my_strcmp( name, store_name ) != 0 ) { #ifdef DEBUG_TREE printf( "program_refresh_update: updating \"%s\"\n", name ); #endif /*DEBUG_TREE*/ gtk_tree_store_set( program->store, &iter, NAME_COLUMN, name, TOOL_POINTER_COLUMN, tool, KIT_POINTER_COLUMN, kit, -1 ); } g_free( store_name ); /* Make sure tool nodes have no children ... this can happen * after some drags. */ if( tool && gtk_tree_model_iter_has_child( GTK_TREE_MODEL( program->store ), &iter ) ) { GtkTreePath *child_path; child_path = gtk_tree_path_copy( path ); gtk_tree_path_down( child_path ); program_refresh_trim( program, child_path ); gtk_tree_path_free( child_path ); } } else { GtkTreeIter parent_iter; GtkTreeIter *piter; #ifdef DEBUG_TREE printf( "program_refresh_update: creating \"%s\"\n", name ); #endif /*DEBUG_TREE*/ /* Get an iter for the parent node, if it exists. */ if( gtk_tree_path_get_depth( path ) > 1 ) { GtkTreePath *parent_path; parent_path = gtk_tree_path_copy( path ); gtk_tree_path_up( parent_path ); gtk_tree_model_get_iter( GTK_TREE_MODEL( program->store ), &parent_iter, parent_path ); gtk_tree_path_free( parent_path ); piter = &parent_iter; } else piter = NULL; gtk_tree_store_append( program->store, &iter, piter ); gtk_tree_store_set( program->store, &iter, NAME_COLUMN, name, TOOL_POINTER_COLUMN, tool, KIT_POINTER_COLUMN, kit, -1 ); } } static void * program_refresh_tool( Tool *tool, Program *program, GtkTreePath *path ) { if( tool->toolitem ) program_refresh_update( program, path, IOBJECT( tool )->name, tool, tool->kit ); else program_refresh_update( program, path, IOBJECT( tool )->name, tool, tool->kit ); gtk_tree_path_next( path ); return( NULL ); } static void * program_refresh_kit( Toolkit *kit, Program *program, GtkTreePath *path ) { program_refresh_update( program, path, IOBJECT( kit )->name, NULL, kit ); gtk_tree_path_down( path ); toolkit_map( kit, (tool_map_fn) program_refresh_tool, program, path ); /* Remove any unused tool nodes. */ program_refresh_trim( program, path ); gtk_tree_path_up( path ); gtk_tree_path_next( path ); return( NULL ); } /* Update the title. */ static void program_title( Program *program ) { char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( program->kit && FILEMODEL( program->kit )->modified ) vips_buf_appendf( &buf, "*" ); vips_buf_appends( &buf, IOBJECT( program->kitg )->name ); if( program->kit ) vips_buf_appendf( &buf, " - %s", IOBJECT( program->kit )->name ); if( program->tool ) { vips_buf_appendf( &buf, " - %s", IOBJECT( program->tool )->name ); if( program->dirty ) { vips_buf_appendf( &buf, " [" ); vips_buf_appendf( &buf, _( "modified" ) ); vips_buf_appendf( &buf, "]" ); } } iwindow_set_title( IWINDOW( program ), "%s", vips_buf_all( &buf ) ); } typedef struct _ProgramRowLookupInfo { Program *program; Model *model; GtkTreeIter *return_iter; gboolean found; } ProgramRowLookupInfo; static gboolean program_row_lookup_sub( GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ProgramRowLookupInfo *info ) { Tool *tool; Toolkit *kit; gtk_tree_model_get( model, iter, TOOL_POINTER_COLUMN, &tool, KIT_POINTER_COLUMN, &kit, -1 ); if( (void *) tool == (void *) info->model || (void *) kit == (void *) info->model ) { *info->return_iter = *iter; info->found = TRUE; return( TRUE ); } return( FALSE ); } /* Point return_iter at the row containing a pointer to the Model. */ static gboolean program_row_lookup( Program *program, Model *model, GtkTreeIter *return_iter ) { ProgramRowLookupInfo info; info.program = program; info.model = model; info.return_iter = return_iter; info.found = FALSE; gtk_tree_model_foreach( GTK_TREE_MODEL( program->store ), (GtkTreeModelForeachFunc) program_row_lookup_sub, &info ); return( info.found ); } static gboolean program_refresh_timeout( gpointer user_data ) { Program *program = PROGRAM( user_data ); iWindow *iwnd = IWINDOW( program ); Model *model = program_get_selected( program ); GtkTreeSelection *select = gtk_tree_view_get_selection( GTK_TREE_VIEW( program->tree ) ); GtkTreePath *path; GtkTreeIter iter; GtkAction *action; program->refresh_timeout = 0; #ifdef DEBUG printf( "program_refresh_timeout\n" ); #endif /*DEBUG*/ /* Block insert/delete/select signals. */ g_signal_handler_block( G_OBJECT( program->store ), program->row_deleted_sid ); g_signal_handler_block( G_OBJECT( program->store ), program->row_inserted_sid ); g_signal_handler_block( G_OBJECT( select ), program->select_changed_sid ); /* Rebuild the tree widget. */ path = gtk_tree_path_new(); gtk_tree_path_down( path ); toolkitgroup_map( program->kitg, (toolkit_map_fn) program_refresh_kit, program, path ); /* Remove any unused kit nodes. */ program_refresh_trim( program, path ); gtk_tree_path_free( path ); g_signal_handler_unblock( G_OBJECT( program->store ), program->row_inserted_sid ); g_signal_handler_unblock( G_OBJECT( program->store ), program->row_deleted_sid ); /* Update title bar. */ program_title( program ); /* Scroll to current kit or tool. */ if( model && program_row_lookup( program, model, &iter ) ) { path = gtk_tree_model_get_path( GTK_TREE_MODEL( program->store ), &iter ); /* Only expand tools ... we want to be able to select kits * without expansion. */ if( IS_TOOL( model ) ) gtk_tree_view_expand_to_path( GTK_TREE_VIEW( program->tree ), path ); gtk_tree_view_set_cursor( GTK_TREE_VIEW( program->tree ), path, NULL, FALSE ); gtk_tree_path_free( path ); } else gtk_tree_selection_unselect_all( select ); g_signal_handler_unblock( G_OBJECT( select ), program->select_changed_sid ); action = gtk_action_group_get_action( iwnd->action_group, "DefBrowser" ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), program->rpane->open ); return( FALSE ); } /* Schedule an update for all our widgets. */ static void program_refresh( Program *program ) { IM_FREEF( g_source_remove, program->refresh_timeout ); /* 10ms to make sure we run after idle (is this right?) */ program->refresh_timeout = g_timeout_add( 10, (GSourceFunc) program_refresh_timeout, program ); } /* Break the tool & kit links. */ static void program_detach( Program *program ) { if( program->tool ) { program->pos = -1; FREESID( program->tool_destroy_sid, program->tool ); program->tool = NULL; } if( program->kit ) { FREESID( program->kit_destroy_sid, program->kit ); program->kit = NULL; } program_refresh( program ); } static void program_find_reset( Program *program ) { FREESID( program->find_sym_destroy_sid, program->find_sym ); program->find_sym = NULL; program->find_start = 0; program->find_end = 0; } static void program_find_destroy_cb( Symbol *sym, Program *program ) { program_find_reset( program ); } static void program_find_note( Program *program, Symbol *sym, int start, int end ) { program_find_reset( program ); program->find_sym = sym; program->find_sym_destroy_sid = g_signal_connect( G_OBJECT( sym ), "destroy", G_CALLBACK( program_find_destroy_cb ), program ); program->find_start = start; program->find_end = end; } static gboolean program_find_pos( Program *program, const char *text, int *start, int *end ) { #ifdef HAVE_GREGEX if( program->regexp ) { GMatchInfo *match; g_regex_match( program->comp, text, 0, &match ); if( g_match_info_fetch_pos( match, 0, start, end ) ) { g_match_info_free( match ); return( TRUE ); } g_match_info_free( match ); } else #endif /*HAVE_GREGEX*/ if( program->csens ) { char *p; if( (p = strstr( text, program->search )) ) { *start = p - text; *end = *start + strlen( program->search ); return( TRUE ); } } else { char *p; if( (p = my_strcasestr( text, program->search )) ) { *start = p - text; *end = *start + strlen( program->search ); return( TRUE ); } } return( FALSE ); } static void * program_find_tool( Tool *tool, Program *program, gboolean *skipping ) { Symbol *sym; if( tool->type != TOOL_SYM ) return( NULL ); sym = tool->sym; /* In search mode? Check if we've found the start point. */ if( *skipping ) { if( sym == program->find_sym || !program->find_sym ) *skipping = FALSE; } /* Reached start point? Check from start onwards. */ if( !*skipping ) { if( sym->expr && sym->expr->compile && program->find_start < strlen( sym->expr->compile->text ) ) { int start, end; if( program_find_pos( program, sym->expr->compile->text + program->find_start, &start, &end ) ) { program_find_note( program, sym, start + program->find_start, end + program->find_start ); return( tool ); } } program_find_reset( program ); } return( NULL ); } static void * program_find_toolkit( Toolkit *kit, Program *program, gboolean *skipping ) { return( icontainer_map( ICONTAINER( kit ), (icontainer_map_fn) program_find_tool, program, &skipping ) ); } static gboolean program_find( Program *program ) { gboolean skipping = TRUE; if( toolkitgroup_map( program->kitg, (toolkit_map_fn) program_find_toolkit, program, &skipping ) ) return( TRUE ); return( FALSE ); } static void program_destroy( GtkObject *object ) { Program *program; g_return_if_fail( object != NULL ); g_return_if_fail( IS_PROGRAM( object ) ); program = PROGRAM( object ); #ifdef DEBUG printf( "program_destroy\n" ); #endif /*DEBUG*/ /* My instance destroy stuff. */ program_detach( program ); UNREF( program->store ); FREESID( program->kitgroup_changed_sid, program->kitg ); FREESID( program->kitgroup_destroy_sid, program->kitg ); IM_FREEF( g_free, program->search ); #ifdef HAVE_GREGEX IM_FREEF( g_regex_unref, program->comp ); #endif /*HAVE_GREGEX*/ program_find_reset( program ); IM_FREEF( g_source_remove, program->refresh_timeout ); program_all = g_slist_remove( program_all, program ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void program_edit_dia_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Tool *tool = TOOL( client ); Stringset *ss = STRINGSET( iwnd ); StringsetChild *name = stringset_child_get( ss, _( "Name" ) ); StringsetChild *file = stringset_child_get( ss, _( "Filename" ) ); char name_text[1024]; char file_text[1024]; if( !get_geditable_string( name->entry, name_text, 1024 ) || !get_geditable_filename( file->entry, file_text, 1024 ) ) { nfn( sys, IWINDOW_ERROR ); return; } if( !tool_new_dia( tool->kit, ICONTAINER( tool )->pos, name_text, file_text ) ) { nfn( sys, IWINDOW_ERROR ); return; } nfn( sys, IWINDOW_YES ); } static void program_edit_dia( Program *program, Tool *tool ) { GtkWidget *ss = stringset_new(); g_assert( tool && tool->type == TOOL_DIA ); stringset_child_new( STRINGSET( ss ), _( "Name" ), IOBJECT( tool )->name, _( "Menu item text" ) ); stringset_child_new( STRINGSET( ss ), _( "Filename" ), FILEMODEL( tool )->filename, _( "Load column from this file" ) ); iwindow_set_title( IWINDOW( ss ), _( "Edit Column Item \"%s\"" ), IOBJECT( tool )->name ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, tool ); idialog_add_ok( IDIALOG( ss ), program_edit_dia_done_cb, _( "Set column item" ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( program ) ); idialog_set_iobject( IDIALOG( ss ), IOBJECT( tool ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } static void program_edit_object_cb( GtkWidget *menu, Program *program ) { Model *model = program_get_selected( program ); if( model && IS_TOOL( model ) && TOOL( model )->type == TOOL_DIA ) program_edit_dia( program, program->tool ); } static gboolean program_is_saveable( Model *model ) { if( !IS_TOOLKIT( model ) ) { error_top( _( "Unable to save." ) ); error_sub( _( "You can only save toolkits, not tools." ) ); return( FALSE ); } if( IS_TOOLKIT( model ) && TOOLKIT( model )->pseudo ) { error_top( _( "Unable to save." ) ); error_sub( _( "You can't save auto-generated toolkits." ) ); return( FALSE ); } return( TRUE ); } static void program_save_object_cb( GtkWidget *menu, Program *program ) { Model *model = program_get_selected( program ); if( model ) { if( program_is_saveable( model ) ) filemodel_inter_save( IWINDOW( program ), FILEMODEL( model ) ); else iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_ERROR ); } } static void program_saveas_object_cb( GtkWidget *menu, Program *program ) { Model *model = program_get_selected( program ); if( model ) { if( program_is_saveable( model ) ) filemodel_inter_saveas( IWINDOW( program ), FILEMODEL( model ) ); else iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_ERROR ); } } static void program_remove_object_cb( GtkWidget *menu, Program *program ) { Model *model = program_get_selected( program ); if( model ) model_check_destroy( GTK_WIDGET( program ), model, NULL ); } static void program_class_init( ProgramClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidget *pane; parent_class = g_type_class_peek_parent( class ); object_class->destroy = program_destroy; /* Create signals. */ /* Init methods. */ pane = program_menu = popup_build( _( "Toolkit menu" ) ); popup_add_but( pane, _( "_Edit" ), POPUP_FUNC( program_edit_object_cb ) ); popup_add_but( pane, GTK_STOCK_SAVE, POPUP_FUNC( program_save_object_cb ) ); popup_add_but( pane, GTK_STOCK_SAVE_AS, POPUP_FUNC( program_saveas_object_cb ) ); menu_add_sep( pane ); popup_add_but( pane, GTK_STOCK_DELETE, POPUP_FUNC( program_remove_object_cb ) ); } /* Some kit/tool has changed ... update everything. */ static void program_kitgroup_changed( Model *model, Program *program ) { #ifdef DEBUG printf( "program_kitgroup_changed:\n" ); #endif /*DEBUG*/ program_refresh( program ); } static void program_kitgroup_destroy( Model *model, Program *program ) { #ifdef DEBUG printf( "program_kitgroup_destroy:\n" ); #endif /*DEBUG*/ /* Our toolkitgroup has gone! Give up on the world. */ program->kitgroup_changed_sid = 0; program->kitgroup_destroy_sid = 0; iwindow_kill( IWINDOW( program ) ); } static void program_init( Program *program ) { program->kitg = NULL; program->text = NULL; program->dirty = FALSE; program->text_hash = 0; program->tree = NULL; program->store = NULL; program->pane_position = PROGRAM_PANE_POSITION; program->rpane_open = FALSE; program->rpane_position = 500; program->refresh_timeout = 0; program->kitgroup_changed_sid = 0; program->kitgroup_destroy_sid = 0; program->kit = NULL; program->kit_destroy_sid = 0; program->tool = NULL; program->pos = -1; program->tool_destroy_sid = 0; program->search = NULL; program->csens = FALSE; program->fromtop = TRUE; #ifdef HAVE_GREGEX program->regexp = FALSE; program->comp = NULL; #endif /*HAVE_GREGEX*/ } GtkType program_get_type( void ) { static GtkType program_type = 0; if( !program_type ) { static const GtkTypeInfo info = { "Program", sizeof( Program ), sizeof( ProgramClass ), (GtkClassInitFunc) program_class_init, (GtkObjectInitFunc) program_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; program_type = gtk_type_unique( TYPE_IWINDOW, &info ); } return( program_type ); } /* The kit we have selected has been destroyed. */ static void program_kit_destroy( Toolkit *kit, Program *program ) { #ifdef DEBUG printf( "program_kit_destroy:\n" ); #endif /*DEBUG*/ g_assert( program->kit == kit ); program_detach( program ); program_refresh( program ); } /* Is a character one of those allowed in nip2 identifers? */ static gboolean is_ident( int ch ) { if( isalnum( ch ) || ch == '_' || ch == '\'' ) return( TRUE ); return( FALSE ); } static void program_text_cursor_position( GtkTextBuffer *buffer, GParamSpec *pspec, Program *program ) { gboolean editable = !program->kit || !program->kit->pseudo; if( program->rpane_open && editable ) { /* Fetch characters left of the cursor while we have stuff * that could be an identifier. */ GtkTextIter start; GtkTextIter cursor; GtkTextIter end; char *line; char *p, *q, *r; /* Get iters for start / cursor / end of line. */ gtk_text_buffer_get_iter_at_mark( buffer, &cursor, gtk_text_buffer_get_insert( buffer ) ); gtk_text_buffer_get_iter_at_line_index( buffer, &start, gtk_text_iter_get_line( &cursor ), 0 ); gtk_text_buffer_get_iter_at_line_index( buffer, &end, gtk_text_iter_get_line( &cursor ), 0 ); gtk_text_iter_forward_to_line_end( &end ); line = gtk_text_buffer_get_text( buffer, &start, &end, FALSE ); p = line + gtk_text_iter_get_line_index( &cursor ); /* Search back from the cursor for the first non-identifier * char. */ for( q = p - 1; q >= line && is_ident( *q ); q-- ) ; q += 1; for( r = p; r < line + strlen( line ) && is_ident( *r ); r++ ) ; *r= '\0'; if( strlen( q ) > 1 ) defbrowser_set_filter( program->defbrowser, q ); g_free( line ); } } static void program_text_changed( GtkTextBuffer *buffer, Program *program ) { if( !program->dirty ) { program->dirty = TRUE; program_refresh( program ); } } static void program_set_text( Program *program, const char *text, gboolean editable ) { GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); guint text_hash = g_str_hash( text ); if( text_hash != program->text_hash ) { /* Stop ::changed from firing, we don't want it to update the * def browser filter. */ g_signal_handlers_block_by_func( text_buffer, G_CALLBACK( program_text_cursor_position ), program ); text_view_set_text( text_view, text, editable ); program->text_hash = text_hash; g_signal_handlers_unblock_by_func( text_buffer, G_CALLBACK( program_text_cursor_position ), program ); } program->dirty = FALSE; } /* Swap text for text for tool. */ static void program_set_text_tool( Program *program, Tool *tool ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); switch( tool->type ) { case TOOL_DIA: case TOOL_SEP: program_set_text( program, "", FALSE ); break; case TOOL_SYM: switch( tool->sym->type ) { case SYM_EXTERNAL: call_usage( &buf, tool->sym->function ); program_set_text( program, vips_buf_all( &buf ), FALSE ); break; case SYM_BUILTIN: builtin_usage( &buf, tool->sym->builtin ); program_set_text( program, vips_buf_all( &buf ), FALSE ); break; case SYM_VALUE: program_set_text( program, tool->sym->expr->compile->text, TRUE ); break; default: g_assert( FALSE ); } break; default: g_assert( FALSE ); } } /* The sym we are editing has been destroyed. */ static void program_tool_destroy( Tool *tool, Program *program ) { #ifdef DEBUG printf( "program_tool_destroy:\n" ); #endif /*DEBUG*/ g_assert( program->tool == tool ); program_detach( program ); program_set_text( program, "", TRUE ); program_refresh( program ); } /* Pick a kit ... but don't touch the text yet. */ static void program_select_kit_sub( Program *program, Toolkit *kit ) { /* None? Pick "untitled". */ if( !kit ) kit = toolkit_by_name( program->kitg, "untitled" ); program_detach( program ); if( kit ) { program->kit = kit; program->kit_destroy_sid = g_signal_connect( G_OBJECT( kit ), "destroy", G_CALLBACK( program_kit_destroy ), program ); } program_refresh( program ); } /* Select a new kit in the tree. */ static void program_select_kit( Program *program, Toolkit *kit ) { program_select_kit_sub( program, kit ); program_set_text( program, "", TRUE ); program_refresh( program ); } /* Select a tool in the tree. */ static void program_select_tool( Program *program, Tool *tool ) { program_detach( program ); if( tool ) { program_select_kit_sub( program, tool->kit ); program->tool = tool; program->pos = ICONTAINER( tool )->pos; program->tool_destroy_sid = g_signal_connect( G_OBJECT( tool ), "destroy", G_CALLBACK( program_tool_destroy ), program ); program_set_text_tool( program, tool ); } program_refresh( program ); } static char * program_get_text( Program *program ) { GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkTextIter start_iter; GtkTextIter end_iter; char *text; gtk_text_buffer_get_start_iter( text_buffer, &start_iter ); gtk_text_buffer_get_end_iter( text_buffer, &end_iter ); text = gtk_text_buffer_get_text( text_buffer, &start_iter, &end_iter, FALSE ); return( text ); } /* Read and parse the text. */ static gboolean program_parse( Program *program ) { char *txt; char buffer[MAX_STRSIZE]; Compile *compile; if( !program->dirty ) return( TRUE ); /* Irritatingly, we need to append a ';'. Also, update the hash, so we * don't set the same text back again if we can help it. */ txt = program_get_text( program ); program->text_hash = g_str_hash( txt ); im_snprintf( buffer, MAX_STRSIZE, "%s;", txt ); IM_FREEF( g_free, txt ); if( strspn( buffer, WHITESPACE ";" ) == strlen( buffer ) ) return( TRUE ); /* Make sure we've got a kit. */ if( !program->kit ) program_select_kit_sub( program, program->kit ); compile = program->kit->kitg->root->expr->compile; #ifdef DEBUG printf( "program_parse: parsing to kit \"%s\", pos %d\n", IOBJECT( program->kit )->name, program->pos ); #endif /*DEBUG*/ /* ... and parse the new text into it. */ attach_input_string( buffer ); if( !parse_onedef( program->kit, program->pos ) ) { text_view_select_text( GTK_TEXT_VIEW( program->text ), input_state.charpos - yyleng, input_state.charpos ); return( FALSE ); } program->dirty = FALSE; if( program->kit ) filemodel_set_modified( FILEMODEL( program->kit ), TRUE ); /* Reselect last_sym, the last thing the parser saw. */ if( compile->last_sym && compile->last_sym->tool ) program_select_tool( program, compile->last_sym->tool ); symbol_recalculate_all(); return( TRUE ); } static void program_tool_new_action_cb( GtkAction *action, Program *program ) { /* Existing text changed? Parse it. */ if( program->dirty && !program_parse( program ) ) { iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_ERROR ); return; } program_select_kit( program, program->kit ); } static void program_toolkit_new_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Stringset *ss = STRINGSET( iwnd ); StringsetChild *name = stringset_child_get( ss, _( "Name" ) ); StringsetChild *caption = stringset_child_get( ss, _( "Caption" ) ); Program *program = PROGRAM( client ); Toolkit *kit; char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); char name_text[1024]; char caption_text[1024]; if( !get_geditable_name( name->entry, name_text, 1024 ) || !get_geditable_string( caption->entry, caption_text, 1024 ) ) { nfn( sys, IWINDOW_ERROR ); return; } /* Make a filename from the name ... user start directory. */ vips_buf_appendf( &buf, "$SAVEDIR" G_DIR_SEPARATOR_S "start" G_DIR_SEPARATOR_S "%s.def", name_text ); kit = toolkit_new_filename( main_toolkitgroup, vips_buf_all( &buf ) ); /* Set caption. */ if( strspn( caption_text, WHITESPACE ) != strlen( caption_text ) ) iobject_set( IOBJECT( kit ), NULL, caption_text ); else iobject_set( IOBJECT( kit ), NULL, "untitled" ); program_select_kit( program, kit ); nfn( sys, IWINDOW_YES ); } static void program_toolkit_new_action_cb( GtkAction *action, Program *program ) { GtkWidget *ss = stringset_new(); stringset_child_new( STRINGSET( ss ), _( "Name" ), "", _( "Set toolkit name here" ) ); stringset_child_new( STRINGSET( ss ), _( "Caption" ), "", _( "Set toolkit caption here" ) ); iwindow_set_title( IWINDOW( ss ), _( "New Toolkit" ) ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, program ); idialog_add_ok( IDIALOG( ss ), program_toolkit_new_done_cb, _( "Create" ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( program ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } static gboolean program_check_kit( Program *program ) { if( !program->kit ) { error_top( _( "Nothing selected." ) ); error_sub( "%s", _( "No toolkit selected." ) ); iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_INFO ); return( FALSE ); } return( TRUE ); } static void program_separator_new_action_cb( GtkAction *action, Program *program ) { Tool *tool; int pos; if( !program_check_kit( program ) ) return; pos = icontainer_pos_last( ICONTAINER( program->kit ) ); tool = tool_new_sep( program->kit, pos + 1 ); program_select_tool( program, tool ); } static void program_column_item_new_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Stringset *ss = STRINGSET( iwnd ); StringsetChild *name = stringset_child_get( ss, _( "Name" ) ); StringsetChild *file = stringset_child_get( ss, _( "Filename" ) ); Program *program = PROGRAM( client ); Tool *tool; int pos; char name_text[1024]; char file_text[1024]; if( !get_geditable_name( name->entry, name_text, 1024 ) || !get_geditable_filename( file->entry, file_text, 1024 ) ) { nfn( sys, IWINDOW_ERROR ); return; } pos = icontainer_pos_last( ICONTAINER( program->kit ) ); tool = tool_new_dia( program->kit, pos + 1, name_text, file_text ); program_select_tool( program, tool ); nfn( sys, IWINDOW_YES ); } static void program_column_item_new_action_cb( GtkAction *action, Program *program ) { GtkWidget *ss; if( !program_check_kit( program ) ) return; ss = stringset_new(); stringset_child_new( STRINGSET( ss ), _( "Name" ), "", _( "Display this name" ) ); stringset_child_new( STRINGSET( ss ), _( "Filename" ), "", _( "Load this file" ) ); iwindow_set_title( IWINDOW( ss ), "New Column Item" ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, program ); idialog_add_ok( IDIALOG( ss ), program_column_item_new_done_cb, _( "Create" ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( program ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } static void program_program_new_action_cb( GtkAction *action, Program *program ) { Program *program2; program2 = program_new( program->kitg ); gtk_widget_show( GTK_WIDGET( program2 ) ); } static void * program_load_file_fn( Filesel *filesel, const char *filename, Program *program, void *b ) { Toolkit *kit; if( !(kit = toolkit_new_from_file( main_toolkitgroup, filename )) ) return( filesel ); program_select_kit( program, kit ); return( NULL ); } /* Callback from load browser. */ static void program_load_file_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Program *program = PROGRAM( client ); if( filesel_map_filename_multi( filesel, (FileselMapFn) program_load_file_fn, program, NULL ) ) { nfn( sys, IWINDOW_ERROR ); return; } symbol_recalculate_all(); nfn( sys, IWINDOW_YES ); } static void program_open_action_cb( GtkAction *action, Program *program ) { GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Load Definition" ) ); filesel_set_flags( FILESEL( filesel ), FALSE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_definition, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( program ) ); filesel_set_done( FILESEL( filesel ), program_load_file_cb, program ); filesel_set_multi( FILESEL( filesel ), TRUE ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } static void program_save_action_cb( GtkAction *action, Program *program ) { if( !program_check_kit( program ) ) return; filemodel_inter_save( IWINDOW( program ), FILEMODEL( program->kit ) ); } static void program_save_as_action_cb( GtkAction *action, Program *program ) { if( !program_check_kit( program ) ) return; filemodel_inter_saveas( IWINDOW( program ), FILEMODEL( program->kit ) ); } static void program_process_action_cb( GtkAction *action, Program *program ) { if( !program_parse( program ) ) iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_ERROR ); } static void program_reload_menus_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { main_reload(); symbol_recalculate_all(); nfn( sys, IWINDOW_YES ); } /* Reload all menus. */ static void program_reload_action_cb( GtkAction *action, Program *program ) { box_yesno( GTK_WIDGET( program ), program_reload_menus_cb, iwindow_true_cb, NULL, iwindow_notify_null, NULL, _( "Reload" ), _( "Reload startup objects?" ), _( "Would you like to reload all startup menus, workspaces " "and plugins now? This may take a few seconds." ) ); } static void program_cut_action_cb( GtkAction *action, Program *program ) { GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkClipboard *clipboard = gtk_widget_get_clipboard( GTK_WIDGET( text_view ), GDK_SELECTION_CLIPBOARD ); gboolean editable = !program->kit || !program->kit->pseudo; gtk_text_buffer_cut_clipboard( text_buffer, clipboard, editable ); } static void program_copy( Program *program ) { GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkClipboard *clipboard = gtk_widget_get_clipboard( GTK_WIDGET( text_view ), GDK_SELECTION_CLIPBOARD ); gtk_text_buffer_copy_clipboard( text_buffer, clipboard ); } static void program_copy_action_cb( GtkAction *action, Program *program ) { program_copy( program ); } static void program_paste_action_cb( GtkAction *action, Program *program ) { GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkClipboard *clipboard = gtk_widget_get_clipboard( GTK_WIDGET( text_view ), GDK_SELECTION_CLIPBOARD ); gboolean editable = !program->kit || !program->kit->pseudo; gtk_text_buffer_paste_clipboard( text_buffer, clipboard, NULL, editable ); } static void program_delete_action_cb( GtkAction *action, Program *program ) { GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); gboolean editable = !program->kit || !program->kit->pseudo; gtk_text_buffer_delete_selection( text_buffer, TRUE, editable ); } static void program_select_all_action_cb( GtkAction *action, Program *program ) { text_view_select_text( GTK_TEXT_VIEW( program->text ), 0, -1 ); } static void program_deselect_all_action_cb( GtkAction *action, Program *program ) { GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); GtkTextMark *mark = gtk_text_buffer_get_insert( text_buffer ); GtkTextIter iter; gtk_text_buffer_get_iter_at_mark( text_buffer, &iter, mark ); gtk_text_buffer_select_range( text_buffer, &iter, &iter ); } static void program_remove_tool_action_cb( GtkAction *action, Program *program ) { Model *model = program_get_selected( program ); if( model && IS_TOOL( model ) ) model_check_destroy( GTK_WIDGET( program ), model, NULL ); else { error_top( _( "No tool selected" ) ); iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_INFO ); } } static void program_remove_toolkit_action_cb( GtkAction *action, Program *program ) { if( !program_check_kit( program ) ) return; model_check_destroy( GTK_WIDGET( program ), MODEL( program->kit ), NULL ); } static void program_find_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Find *find = FIND( iwnd ); Program *program = PROGRAM( client ); IM_FREEF( g_free, program->search ); program->search = gtk_editable_get_chars( GTK_EDITABLE( find->search ), 0, -1 ); program->csens = GTK_TOGGLE_BUTTON( find->csens )->active; program->fromtop = GTK_TOGGLE_BUTTON( find->fromtop )->active; #ifdef HAVE_GREGEX program->regexp = GTK_TOGGLE_BUTTON( find->regexp )->active; if( program->regexp ) { GRegexCompileFlags cflags = 0; GRegexMatchFlags mflags = 0; if( !program->csens ) cflags |= G_REGEX_CASELESS; IM_FREEF( g_regex_unref, program->comp ); if( !(program->comp = g_regex_new( program->search, cflags, mflags, NULL )) ) { error_top( _( "Parse error." ) ); error_sub( _( "Bad regular expression." ) ); nfn( sys, IWINDOW_ERROR ); return; } } #endif /*HAVE_GREGEX*/ if( program->fromtop ) program_find_reset( program ); else program->find_start += 1; if( program_find( program ) ) { program_select_tool( program, program->find_sym->tool ); text_view_select_text( GTK_TEXT_VIEW( program->text ), program->find_start, program->find_end ); } else { error_top( _( "Not found." ) ); error_sub( _( "No match found for \"%s\"." ), program->search ); nfn( sys, IWINDOW_ERROR ); return; } nfn( sys, IWINDOW_YES ); } static void program_find_action_cb( GtkAction *action, Program *program ) { GtkWidget *find = find_new(); iwindow_set_title( IWINDOW( find ), _( "Find in all Toolkits" ) ); idialog_set_callbacks( IDIALOG( find ), iwindow_true_cb, NULL, NULL, program ); idialog_add_ok( IDIALOG( find ), program_find_done_cb, GTK_STOCK_FIND ); iwindow_set_parent( IWINDOW( find ), GTK_WIDGET( program ) ); idialog_set_cancel_text( IDIALOG( find ), GTK_STOCK_CLOSE ); iwindow_build( IWINDOW( find ) ); if( program->search ) set_gentry( FIND( find )->search, "%s", program->search ); set_tooltip( FIND( find )->search, _( "Enter search string here" ) ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( FIND( find )->csens ), program->csens ); #ifdef HAVE_GREGEX gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( FIND( find )->regexp ), program->regexp ); #endif /*HAVE_GREGEX*/ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( FIND( find )->fromtop ), program->fromtop ); gtk_widget_show( find ); } static void program_find_again_action_cb( GtkAction *action, Program *program ) { if( !program->search ) return; if( program->find_sym ) program->find_start += 1; if( program_find( program ) ) { program_select_tool( program, program->find_sym->tool ); text_view_select_text( GTK_TEXT_VIEW( program->text ), program->find_start, program->find_end ); } else { error_top( _( "Not found." ) ); iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_INFO ); } } static void program_goto_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Program *program = PROGRAM( client ); Stringset *ss = STRINGSET( iwnd ); StringsetChild *name = stringset_child_get( ss, _( "Name" ) ); Symbol *sym; char name_text[1024]; if( !get_geditable_string( name->entry, name_text, 1024 ) ) { nfn( sys, IWINDOW_ERROR ); return; } if( !(sym = compile_lookup( program->kitg->root->expr->compile, name_text )) ) { error_top( _( "Not found." ) ); error_sub( _( "No top-level symbol called \"%s\"." ), name_text ); nfn( sys, IWINDOW_ERROR ); return; } if( !sym->tool ) { error_top( _( "Not found." ) ); error_sub( _( "Symbol \"%s\" has no tool inforation." ), name_text ); nfn( sys, IWINDOW_ERROR ); return; } if( !program_select( program, MODEL( sym->tool ) ) ) { nfn( sys, IWINDOW_ERROR ); return; } nfn( sys, IWINDOW_YES ); } static void program_goto_action_cb( GtkAction *action, Program *program ) { GtkWidget *ss = stringset_new(); StringsetChild *name; name = stringset_child_new( STRINGSET( ss ), _( "Name" ), "", _( "Go to definition of this symbol" ) ); iwindow_set_title( IWINDOW( ss ), _( "Go to Definition" ) ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, program ); idialog_add_ok( IDIALOG( ss ), program_goto_done_cb, GTK_STOCK_JUMP_TO ); idialog_set_pinup( IDIALOG( ss ), TRUE ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( program ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); /* Now try to paste the selection into the name widget. FIXME ... get rid of this, have a right-button menu on the text widget which includes a 'go to def' item. or could make sym names into hyperlinks? see text demo example */ program_copy( program ); gtk_editable_paste_clipboard( GTK_EDITABLE( name->entry ) ); } static void program_info_action_cb( GtkAction *action, Program *program ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); program_info( program, &buf ); error_top( _( "Object information." ) ); error_sub( "%s", vips_buf_all( &buf ) ); iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_INFO ); } static void program_trace_action_cb( GtkAction *action, Program *program ) { (void) trace_new(); } static void program_errorreport_action_cb( GtkAction *action, Program *program ) { iError *ierror; ierror = ierror_new( program->kitg ); gtk_widget_show( GTK_WIDGET( ierror ) ); #ifdef DEBUG /* Dump VIPS memory usage info for debugging. */ im__print_all(); #endif /*DEBUG*/ } static void program_tool_help_action_cb( GtkAction *action, Program *program ) { if( program->tool && program->tool->type == TOOL_SYM && program->kit && program->kit->pseudo ) { switch( program->tool->sym->type ) { case SYM_EXTERNAL: /* With vips7 we displayed the man page. When we go * properly vips8, display the API docs. * char txt[512]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_appendf( &buf, "file://" VIPS_DOCPATH "/man/%s.3.html", IOBJECT( program->tool->sym )->name ); box_url( GTK_WIDGET( program ), vips_buf_all( &buf ) ); * */ break; case SYM_BUILTIN: box_help( GTK_WIDGET( program ), "tb:builtin" ); break; default: break; } } else { error_top( _( "No documentation available." ) ); error_sub( "%s", _( "On-line documentation is only currently " "available for VIPS functions and nip builtins." ) ); iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_INFO ); } } /* Expose/hide the definition browser. */ static void program_defbrowser_action_cb( GtkToggleAction *action, Program *program ) { if( gtk_toggle_action_get_active( action ) ) pane_animate_open( program->rpane ); else pane_animate_closed( program->rpane ); } /* Our actions. */ static GtkActionEntry program_actions[] = { /* Menu items. */ { "DebugMenu", NULL, "_Debug" }, /* Actions. */ { "NewTool", GTK_STOCK_NEW, N_( "New _Tool" ), NULL, N_( "Make a new tool" ), G_CALLBACK( program_tool_new_action_cb ) }, { "NewToolkit", GTK_STOCK_NEW, N_( "New Tool_kit" ), NULL, N_( "Make a new toolkit" ), G_CALLBACK( program_toolkit_new_action_cb ) }, { "NewSeparator", GTK_STOCK_NEW, N_( "New _Separator" ), NULL, N_( "Make a new separator" ), G_CALLBACK( program_separator_new_action_cb ) }, { "NewColumnItem", GTK_STOCK_NEW, N_( "New _Column Item" ), NULL, N_( "Make a new column item" ), G_CALLBACK( program_column_item_new_action_cb ) }, { "NewProgram", GTK_STOCK_NEW, N_( "New _Program Window" ), NULL, N_( "Make a new program window" ), G_CALLBACK( program_program_new_action_cb ) }, { "Open", GTK_STOCK_OPEN, N_( "_Open Toolkit" ), NULL, N_( "_Open toolkit" ), G_CALLBACK( program_open_action_cb ) }, { "Save", GTK_STOCK_SAVE, N_( "Save Toolkit" ), NULL, N_( "_Save toolkit" ), G_CALLBACK( program_save_action_cb ) }, { "SaveAs", GTK_STOCK_SAVE_AS, N_( "Save Toolkit _As" ), NULL, N_( "Save toolkit as" ), G_CALLBACK( program_save_as_action_cb ) }, { "Process", NULL, N_( "_Process" ), NULL, N_( "Process text" ), G_CALLBACK( program_process_action_cb ) }, { "Reload", NULL, N_( "_Reload All Toolkits" ), NULL, N_( "Remove and reload all startup data" ), G_CALLBACK( program_reload_action_cb ) }, { "Cut", GTK_STOCK_CUT, N_( "C_ut" ), NULL, N_( "Cut selected text" ), G_CALLBACK( program_cut_action_cb ) }, { "Copy", GTK_STOCK_COPY, N_( "_Copy" ), NULL, N_( "Copy selected text" ), G_CALLBACK( program_copy_action_cb ) }, { "Paste", GTK_STOCK_PASTE, N_( "_Paste" ), NULL, N_( "Paste selected text" ), G_CALLBACK( program_paste_action_cb ) }, { "Delete", GTK_STOCK_DELETE, N_( "_Delete" ), NULL, N_( "Delete selected text" ), G_CALLBACK( program_delete_action_cb ) }, { "SelectAll", NULL, N_( "Select _All" ), NULL, N_( "Select all text" ), G_CALLBACK( program_select_all_action_cb ) }, { "DeselectAll", NULL, N_( "Dese_lect All" ), NULL, N_( "Deselect all text" ), G_CALLBACK( program_deselect_all_action_cb ) }, { "DeleteTool", NULL, N_( "Delete _Tool" ), NULL, N_( "Delete current tool" ), G_CALLBACK( program_remove_tool_action_cb ) }, { "DeleteToolkit", NULL, N_( "Delete Tool_kit" ), NULL, N_( "Delete current toolkit" ), G_CALLBACK( program_remove_toolkit_action_cb ) }, { "Find", GTK_STOCK_FIND, N_( "_Find" ), NULL, N_( "Find text in toolkits" ), G_CALLBACK( program_find_action_cb ) }, { "FindNext", NULL, N_( "Find _Next" ), "G", N_( "Find text again" ), G_CALLBACK( program_find_again_action_cb ) }, { "JumpTo", GTK_STOCK_JUMP_TO, N_( "_Jump To Definition" ), NULL, N_( "Jump to definition" ), G_CALLBACK( program_goto_action_cb ) }, { "Info", NULL, N_( "_Info" ), NULL, N_( "Info on selected object" ), G_CALLBACK( program_info_action_cb ) }, { "Trace", NULL, N_( "_Trace" ), NULL, N_( "Make a new trace window" ), G_CALLBACK( program_trace_action_cb ) }, { "Errors", NULL, N_( "_Errors" ), NULL, N_( "Show all errors" ), G_CALLBACK( program_errorreport_action_cb ) }, { "HelpTool", NULL, N_( "Help on _Tool" ), NULL, N_( "View docs for this tool" ), G_CALLBACK( program_tool_help_action_cb ) } }; static GtkToggleActionEntry program_toggle_actions[] = { { "DefBrowser", NULL, N_( "Definition _Browser" ), NULL, N_( "Show definition browser" ), G_CALLBACK( program_defbrowser_action_cb ), FALSE } }; static const char *program_menubar_ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static void program_lpane_changed_cb( Pane *pane, Program *program ) { } static void program_rpane_changed_cb( Pane *pane, Program *program ) { if( program->rpane_open != pane->open || program->rpane_position != pane->user_position ) { program->rpane_open = pane->open; program->rpane_position = pane->user_position; program_refresh( program ); } } gboolean program_select( Program *program, Model *model ) { /* Existing text changed? Parse it. */ if( program->dirty && !program_parse( program ) ) return( FALSE ); if( model ) { if( IS_TOOL( model ) ) program_select_tool( program, TOOL( model ) ); else if( IS_TOOLKIT( model ) ) program_select_kit( program, TOOLKIT( model ) ); } return( TRUE ); } /* Select a row from an iter. */ static void program_select_row( Program *program, GtkTreeIter *iter ) { Tool *tool; Toolkit *kit; Model *model; gtk_tree_model_get( GTK_TREE_MODEL( program->store ), iter, TOOL_POINTER_COLUMN, &tool, KIT_POINTER_COLUMN, &kit, -1 ); if( tool ) model = MODEL( tool ); else model = MODEL( kit ); if( !program_select( program, model ) ) iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_ERROR ); } static void program_row_collapsed_cb( GtkTreeView *tree, GtkTreeIter *iter, GtkTreePath *path, Program *program ) { Toolkit *kit; #ifdef DEBUG printf( "program_row_collapsed_cb:\n" ); printf( " path = %s\n", gtk_tree_path_to_string( path ) ); #endif /*DEBUG*/ gtk_tree_model_get( GTK_TREE_MODEL( program->store ), iter, KIT_POINTER_COLUMN, &kit, -1 ); /* If we have collapsed the kit containing the currently selected * tool, the kit will just bounce open again when we refresh the tree. * Unselect the tool. */ if( program->kit == kit ) program_select_kit( program, kit ); } static void program_selection_changed_cb( GtkTreeSelection *select, Program *program ) { GtkTreeIter iter; GtkTreeModel *model; #ifdef DEBUG printf( "program_selection_changed_cb:\n" ); #endif /*DEBUG*/ if( gtk_tree_selection_get_selected( select, &model, &iter ) ) { #ifdef DEBUG printf( " selection = %s\n", gtk_tree_path_to_string ( gtk_tree_model_get_path( model, &iter ) ) ); #endif /*DEBUG*/ program_select_row( program, &iter ); } program_refresh( program ); } static gboolean program_tree_event_cb( GtkTreeView *tree, GdkEvent *ev, Program *program ) { GtkTreePath *path; gboolean handled = FALSE; if( ev->type == GDK_BUTTON_PRESS && ev->button.button == 3 && gtk_tree_view_get_path_at_pos( tree, ev->button.x, ev->button.y, &path, NULL, NULL, NULL ) ) { GtkTreeIter iter; gtk_tree_model_get_iter( GTK_TREE_MODEL( program->store ), &iter, path ); program_select_row( program, &iter ); gtk_tree_path_free( path ); popup_link( GTK_WIDGET( program ), program_menu, NULL ); popup_show( GTK_WIDGET( program ), ev ); handled = TRUE; } return( handled ); } static void program_row_inserted_cb( GtkTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, Program *program ) { GtkTreeIter iter2; GtkTreeIter iter3; #ifdef DEBUG printf( "program_row_inserted_cb:\n" ); printf( " path = %s\n", gtk_tree_path_to_string( path ) ); #endif /*DEBUG*/ program->to_pos = -1; program->to_kit = NULL; switch( gtk_tree_path_get_depth( path ) ) { case 3: program->to_pos = gtk_tree_path_get_indices( path )[1]; gtk_tree_model_iter_parent( GTK_TREE_MODEL( program->store ), &iter2, iter ); gtk_tree_model_iter_parent( GTK_TREE_MODEL( program->store ), &iter3, &iter2 ); gtk_tree_model_get( GTK_TREE_MODEL( program->store ), &iter3, KIT_POINTER_COLUMN, &program->to_kit, -1 ); break; case 2: program->to_pos = gtk_tree_path_get_indices( path )[1]; gtk_tree_model_iter_parent( GTK_TREE_MODEL( program->store ), &iter2, iter ); gtk_tree_model_get( GTK_TREE_MODEL( program->store ), &iter2, KIT_POINTER_COLUMN, &program->to_kit, -1 ); break; case 1: program->to_pos = -1; gtk_tree_model_get( GTK_TREE_MODEL( program->store ), iter, KIT_POINTER_COLUMN, &program->to_kit, -1 ); break; } #ifdef DEBUG if( program->to_kit ) { printf( " to_kit = " ); iobject_print( IOBJECT( program->to_kit ) ); } else printf( " to_kit = NULL\n" ); printf( " to_pos = %d\n", program->to_pos ); #endif /*DEBUG*/ } static void program_row_deleted_cb( GtkTreeModel *treemodel, GtkTreePath *path, Program *program ) { #ifdef DEBUG printf( "program_row_deleted_cb:\n" ); printf( " delete path = %s\n", gtk_tree_path_to_string( path ) ); #endif /*DEBUG*/ if( !program->to_kit || !program->tool ) { error_top( _( "Bad drag." ) ); error_sub( "%s", _( "Sorry, you can only drag tools between toolkits. " "You can't reorder toolkits, you can't nest toolkits " "and you can't drag tools to the top level." ) ); iwindow_alert( GTK_WIDGET( program ), GTK_MESSAGE_INFO ); return; } #ifdef DEBUG printf( " to_kit = " ); iobject_print( IOBJECT( program->to_kit ) ); printf( " to_pos = %d\n", program->to_pos ); printf( " selected tool = " ); iobject_print( IOBJECT( program->tool ) ); #endif /*DEBUG*/ /* Move tool. */ g_object_ref( G_OBJECT( program->tool ) ); icontainer_child_remove( ICONTAINER( program->tool ) ); icontainer_child_add( ICONTAINER( program->to_kit ), ICONTAINER( program->tool ), program->to_pos ); g_object_unref( G_OBJECT( program->tool ) ); filemodel_set_modified( FILEMODEL( program->to_kit ), TRUE ); iobject_changed( IOBJECT( program->tool ) ); } static void program_edit_map_cb( GtkWidget *widget, Program *program ) { iWindow *iwnd = IWINDOW( program ); GtkClipboard *clipboard = gtk_widget_get_clipboard( GTK_WIDGET( program ), GDK_SELECTION_CLIPBOARD ); GtkTextView *text_view = GTK_TEXT_VIEW( program->text ); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( text_view ); gboolean editable = !program->kit || !program->kit->pseudo; gboolean available = gtk_clipboard_wait_is_text_available( clipboard ); gboolean selected = gtk_text_buffer_get_selection_bounds( text_buffer, NULL, NULL ); GtkAction *action; action = gtk_action_group_get_action( iwnd->action_group, "Paste" ); g_object_set( G_OBJECT( action ), "sensitive", available && editable, NULL ); action = gtk_action_group_get_action( iwnd->action_group, "Copy" ); g_object_set( G_OBJECT( action ), "sensitive", selected, NULL ); action = gtk_action_group_get_action( iwnd->action_group, "Cut" ); g_object_set( G_OBJECT( action ), "sensitive", selected && editable, NULL ); action = gtk_action_group_get_action( iwnd->action_group, "Delete" ); g_object_set( G_OBJECT( action ), "sensitive", selected && editable, NULL ); action = gtk_action_group_get_action( iwnd->action_group, "DeselectAll" ); g_object_set( G_OBJECT( action ), "sensitive", selected, NULL ); } static PangoTabArray * program_tabs_new( void ) { const int ntabs = 20; const int tab_width = 30; /* in pixels, about 4 chars */ PangoTabArray *tabs = pango_tab_array_new( ntabs, TRUE ); int i; for( i = 0; i < ntabs; i++ ) pango_tab_array_set_tab( tabs, i, PANGO_TAB_LEFT, i * tab_width ); return( tabs ); } GtkWidget * program_text_new( void ) { PangoFontDescription *font_desc; PangoTabArray *tabs; GtkWidget *text; text = gtk_text_view_new(); font_desc = pango_font_description_from_string( "Monospace" ); gtk_widget_modify_font( text, font_desc ); pango_font_description_free( font_desc ); tabs = program_tabs_new(); gtk_text_view_set_tabs( GTK_TEXT_VIEW( text ), tabs ); pango_tab_array_free( tabs ); return( text ); } static void program_build( Program *program, GtkWidget *vbox ) { iWindow *iwnd = IWINDOW( program ); GError *error; GtkWidget *mbar; GtkWidget *item; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *select; GtkWidget *swin; Panechild *panechild; GtkWidget *ebox; /* Make main menu bar */ gtk_action_group_add_actions( iwnd->action_group, program_actions, G_N_ELEMENTS( program_actions ), GTK_WINDOW( program ) ); gtk_action_group_add_toggle_actions( iwnd->action_group, program_toggle_actions, G_N_ELEMENTS( program_toggle_actions ), GTK_WINDOW( program ) ); error = NULL; if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager, program_menubar_ui_description, -1, &error ) ) { g_message( "building menus failed: %s", error->message ); g_error_free( error ); exit( EXIT_FAILURE ); } mbar = gtk_ui_manager_get_widget( iwnd->ui_manager, "/ProgramMenubar" ); gtk_box_pack_start( GTK_BOX( vbox ), mbar, FALSE, FALSE, 0 ); gtk_widget_show( mbar ); /* On map of the edit menu, rethink cut/copy/paste sensitivity. */ item = gtk_ui_manager_get_widget( iwnd->ui_manager, "/ProgramMenubar/EditMenu/Cut" ); item = gtk_widget_get_parent( GTK_WIDGET( item ) ); gtk_signal_connect( GTK_OBJECT( item ), "map", GTK_SIGNAL_FUNC( program_edit_map_cb ), program ); /* This will set to NULL if we don't have infobar support. */ if( (IWINDOW( program )->infobar = infobar_new()) ) gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( IWINDOW( program )->infobar ), FALSE, FALSE, 0 ); program->rpane = pane_new( PANE_HIDE_RIGHT ); g_signal_connect( program->rpane, "changed", G_CALLBACK( program_rpane_changed_cb ), program ); gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( program->rpane ), TRUE, TRUE, 0 ); gtk_widget_show( GTK_WIDGET( program->rpane ) ); program->lpane = pane_new( PANE_HIDE_LEFT ); g_signal_connect( program->lpane, "changed", G_CALLBACK( program_lpane_changed_cb ), program ); gtk_paned_set_position( GTK_PANED( program->lpane ), program->pane_position ); gtk_paned_pack1( GTK_PANED( program->rpane ), GTK_WIDGET( program->lpane ), TRUE, FALSE ); gtk_widget_show( GTK_WIDGET( program->lpane ) ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_paned_pack1( GTK_PANED( program->lpane ), swin, FALSE, FALSE ); gtk_widget_show( swin ); program->store = gtk_tree_store_new( N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER ); program->row_inserted_sid = g_signal_connect( G_OBJECT( program->store ), "row_inserted", G_CALLBACK( program_row_inserted_cb ), program ); program->row_deleted_sid = g_signal_connect( G_OBJECT( program->store ), "row_deleted", G_CALLBACK( program_row_deleted_cb ), program ); program->tree = gtk_tree_view_new_with_model( GTK_TREE_MODEL( program->store ) ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Tool" ), renderer, "text", NAME_COLUMN, NULL ); gtk_tree_view_append_column( GTK_TREE_VIEW( program->tree ), column ); g_signal_connect( G_OBJECT( program->tree ), "row_collapsed", G_CALLBACK( program_row_collapsed_cb ), program ); gtk_container_add( GTK_CONTAINER( swin ), program->tree ); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( program->tree ), FALSE ); gtk_tree_view_set_enable_search( GTK_TREE_VIEW( program->tree ), TRUE ); gtk_tree_view_set_reorderable( GTK_TREE_VIEW( program->tree ), TRUE ); select = gtk_tree_view_get_selection( GTK_TREE_VIEW( program->tree ) ); gtk_tree_selection_set_mode( select, GTK_SELECTION_SINGLE ); program->select_changed_sid = g_signal_connect( G_OBJECT( select ), "changed", G_CALLBACK( program_selection_changed_cb ), program ); gtk_signal_connect( GTK_OBJECT( program->tree ), "event", GTK_SIGNAL_FUNC( program_tree_event_cb ), program ); gtk_widget_show( program->tree ); /* Toolkit Browser pane. */ panechild = panechild_new( program->rpane, _( "Definition Browser" ) ); /* Have to put toolkitbrowser in an ebox so the search entry gets * clipped to the pane size. */ ebox = gtk_event_box_new(); gtk_container_add( GTK_CONTAINER( panechild ), GTK_WIDGET( ebox ) ); gtk_widget_show( ebox ); program->defbrowser = defbrowser_new(); vobject_link( VOBJECT( program->defbrowser ), IOBJECT( program->kitg ) ); defbrowser_set_program( program->defbrowser, program ); gtk_container_add( GTK_CONTAINER( ebox ), GTK_WIDGET( program->defbrowser ) ); gtk_widget_show( GTK_WIDGET( program->defbrowser ) ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_paned_pack2( GTK_PANED( program->lpane ), swin, TRUE, TRUE ); gtk_widget_show( swin ); program->text = program_text_new(); g_signal_connect( gtk_text_view_get_buffer( GTK_TEXT_VIEW( program->text ) ), "notify::cursor-position", G_CALLBACK( program_text_cursor_position ), program ); g_signal_connect( gtk_text_view_get_buffer( GTK_TEXT_VIEW( program->text ) ), "changed", G_CALLBACK( program_text_changed ), program ); gtk_container_add( GTK_CONTAINER( swin ), program->text ); gtk_widget_show( program->text ); gtk_widget_grab_focus( program->text ); } static void program_popdown( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Program *program = PROGRAM( iwnd ); prefs_set( "PROGRAM_PANE_POSITION", "%d", gtk_paned_get_position( GTK_PANED( program->lpane ) ) ); /* We can't parse in popdown, we may have lost too much of the rest of * nip2 before here. */ nfn( sys, IWINDOW_YES ); } static void program_link( Program *program, Toolkitgroup *kitg ) { program->kitg = kitg; program_title( program ); iwindow_set_size_prefs( IWINDOW( program ), "PROGRAM_WIDTH", "PROGRAM_HEIGHT" ); iwindow_set_build( IWINDOW( program ), (iWindowBuildFn) program_build, NULL, NULL, NULL ); iwindow_set_popdown( IWINDOW( program ), program_popdown, NULL ); iwindow_build( IWINDOW( program ) ); program_all = g_slist_prepend( program_all, program ); program_refresh( program ); program->kitgroup_changed_sid = g_signal_connect( G_OBJECT( program->kitg ), "changed", G_CALLBACK( program_kitgroup_changed ), program ); program->kitgroup_destroy_sid = g_signal_connect( G_OBJECT( program->kitg ), "destroy", G_CALLBACK( program_kitgroup_destroy ), program ); pane_set_state( program->rpane, program->rpane_open, program->rpane_position ); } Program * program_new( Toolkitgroup *kitg ) { Program *program = gtk_type_new( TYPE_PROGRAM ); program_link( program, kitg ); return( program ); } ================================================ FILE: src/program.h ================================================ /* Decls for program.c ... edit window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_PROGRAM (program_get_type()) #define PROGRAM( obj ) (GTK_CHECK_CAST( (obj), TYPE_PROGRAM, Program )) #define PROGRAM_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_PROGRAM, ProgramClass )) #define IS_PROGRAM( obj ) (GTK_CHECK_TYPE( (obj), TYPE_PROGRAM )) #define IS_PROGRAM_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_PROGRAM )) struct _Program { iWindow parent_class; /* The set of kits we manage. */ Toolkitgroup *kitg; GtkWidget *text; gboolean dirty; /* Has the text changed since we set it */ guint text_hash; /* Hash of the last text we set */ GtkWidget *tree; Pane *lpane; int pane_position; Pane *rpane; int rpane_position; gboolean rpane_open; Defbrowser *defbrowser; guint refresh_timeout; /* Timeout for UI refresh */ guint select_changed_sid; guint row_inserted_sid; guint row_deleted_sid; /* Track during drags. */ Toolkit *to_kit; int to_pos; /* Store for kit/tool view. */ GtkTreeStore *store; /* Listen for all kit changes here. */ guint kitgroup_changed_sid; guint kitgroup_destroy_sid; /* The current kit. */ Toolkit *kit; guint kit_destroy_sid; /* The selected tool. */ Tool *tool; int pos; /* Position of tool in kit */ guint tool_destroy_sid; /* Current search settings. */ char *search; gboolean csens; /* Case sensitive */ gboolean fromtop; /* Start search from beginning again */ #ifdef HAVE_GREGEX gboolean regexp; /* Interpret as regexp */ GRegex *comp; /* Compiled pattern */ #endif /*HAVE_GREGEX*/ /* Current search position. */ Symbol *find_sym; /* Tool containing search point */ size_t find_start; /* Offset into tool text of found string */ size_t find_end; guint find_sym_destroy_sid;/* Watch for find_sym death here */ }; typedef struct _ProgramClass { iWindowClass parent_class; /* My methods. */ } ProgramClass; GtkType program_get_type( void ); GtkWidget *program_text_new( void ); Program *program_new( Toolkitgroup *kitg ); gboolean program_select( Program *program, Model *model ); ================================================ FILE: src/progress.c ================================================ /* Handle feedback about eval progress. */ /* 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 watch) any later version. This program is distributed in the hope that 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_MEMUSE #define DEBUG */ #include "ip.h" static iContainerClass *progress_parent_class = NULL; /* Our signals. */ enum { SIG_BEGIN, /* Switch to busy state */ SIG_UPDATE, /* Busy progress update */ SIG_END, /* End busy state */ SIG_LAST }; static guint progress_signals[SIG_LAST] = { 0 }; /* Delay before we start showing busy feedback. */ static const double progress_busy_delay = 1.0; /* Delay between busy updates. */ static const double progress_update_interval = 0.1; void progress_begin( void ) { Progress *progress = progress_get(); g_assert( progress->count >= 0 ); #ifdef DEBUG printf( "progress_begin: %d\n", progress->count ); #endif /*DEBUG*/ progress->count += 1; if( progress->count == 1 ) { g_timer_start( progress->busy_timer ); g_timer_start( progress->update_timer ); #ifdef DEBUG_MEMUSE printf( "progress_begin:\n" ); im__print_all(); #endif /*DEBUG_MEMUSE*/ } } static void progress_update( Progress *progress ) { /* Don't show the process and cancel button for a bit. */ if( progress->count ) { double elapsed = g_timer_elapsed( progress->busy_timer, NULL ); if( !progress->busy && elapsed > progress_busy_delay ) { #ifdef DEBUG printf( "progress_update: displaying progress bar\n" ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( progress ), progress_signals[SIG_BEGIN], 0 ); progress->busy = TRUE; } } /* Update regularly, even if we're not inside a begin/end * block. */ if( g_timer_elapsed( progress->update_timer, NULL ) > progress_update_interval ) { gboolean cancel; #ifdef DEBUG printf( "progress_update:\n" ); #endif /*DEBUG*/ g_timer_start( progress->update_timer ); /* Overwrite the message if we're cancelling. */ if( progress->cancel ) { vips_buf_rewind( &progress->feedback ); vips_buf_appends( &progress->feedback, _( "Cancelling" ) ); vips_buf_appends( &progress->feedback, " ..." ); } cancel = FALSE; g_signal_emit( progress, progress_signals[SIG_UPDATE], 0, &cancel ); if( cancel ) progress->cancel = TRUE; /* Rather dangerous, but we need this to give nice updates * for the feedback thing. */ process_events(); #ifdef DEBUG_MEMUSE printf( "progress_update:\n" ); im__print_all(); #endif /*DEBUG_MEMUSE*/ } } gboolean progress_update_percent( int percent, int eta ) { Progress *progress = progress_get(); vips_buf_rewind( &progress->feedback ); if( eta > 30 ) { int minutes = (eta + 30) / 60; vips_buf_appendf( &progress->feedback, ngettext( "%d minute left", "%d minutes left", minutes ), minutes ); } else if( eta > 5 ) vips_buf_appendf( &progress->feedback, ngettext( "%d second left", "%d seconds left", eta ), eta ); else /* The empty string changes the height of the progress bar * argh. */ vips_buf_appendf( &progress->feedback, " " ); progress->percent = percent; progress_update( progress ); return( progress->cancel ); } gboolean progress_update_expr( Expr *expr ) { Progress *progress = progress_get(); vips_buf_rewind( &progress->feedback ); vips_buf_appends( &progress->feedback, _( "Calculating" ) ); vips_buf_appends( &progress->feedback, " " ); if( expr ) expr_name( expr, &progress->feedback ); else vips_buf_appends( &progress->feedback, symbol_get_last_calc() ); vips_buf_appends( &progress->feedback, " ..." ); progress->percent = 0; progress_update( progress ); return( progress->cancel ); } gboolean progress_update_loading( int percent, const char *filename ) { Progress *progress = progress_get(); vips_buf_rewind( &progress->feedback ); vips_buf_appends( &progress->feedback, _( "Loading" ) ); vips_buf_appendf( &progress->feedback, " \"%s\"", filename ); progress->percent = percent; progress_update( progress ); return( progress->cancel ); } gboolean progress_update_tick( void ) { Progress *progress = progress_get(); progress_update( progress ); return( progress->cancel ); } void progress_end( void ) { Progress *progress = progress_get(); progress->count -= 1; #ifdef DEBUG printf( "progress_end: %d\n", progress->count ); #endif /*DEBUG*/ g_assert( progress->count >= 0 ); if( !progress->count ) { if( progress->busy ) g_signal_emit( G_OBJECT( progress ), progress_signals[SIG_END], 0 ); progress->cancel = FALSE; progress->busy = FALSE; #ifdef DEBUG_MEMUSE printf( "progress_end:\n" ); im__print_all(); #endif /*DEBUG_MEMUSE*/ } } static void progress_class_init( ProgressClass *class ) { progress_parent_class = g_type_class_peek_parent( class ); progress_signals[SIG_BEGIN] = g_signal_new( "begin", G_OBJECT_CLASS_TYPE( class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ProgressClass, begin ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); progress_signals[SIG_UPDATE] = g_signal_new( "update", G_OBJECT_CLASS_TYPE( class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ProgressClass, update ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER ); progress_signals[SIG_END] = g_signal_new( "end", G_OBJECT_CLASS_TYPE( class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( ProgressClass, end ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } static void progress_init( Progress *progress ) { #ifdef DEBUG printf( "progress_init\n" ); #endif /*DEBUG*/ progress->count = 0; progress->busy_timer = g_timer_new(); progress->update_timer = g_timer_new(); progress->cancel = FALSE; progress->busy = FALSE; vips_buf_init_static( &progress->feedback, progress->buf, PROGRESS_FEEDBACK_SIZE ); } GType progress_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ProgressClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) progress_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Progress ), 32, /* n_preallocs */ (GInstanceInitFunc) progress_init, }; type = g_type_register_static( TYPE_IOBJECT, "Progress", &info, 0 ); } return( type ); } static Progress * progress_new( void ) { Progress *progress = PROGRESS( g_object_new( TYPE_PROGRESS, NULL ) ); return( progress ); } Progress * progress_get( void ) { static Progress *progress = NULL; if( !progress ) progress = progress_new(); return( progress ); } ================================================ FILE: src/progress.h ================================================ /* Handle feedback about eval progress. */ /* 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 watch) any later version. This program is distributed in the hope that 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* The max size of the feedback message. */ #define PROGRESS_FEEDBACK_SIZE (100) #define TYPE_PROGRESS (progress_get_type()) #define PROGRESS( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_PROGRESS, Progress )) #define PROGRESS_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_PROGRESS, ProgressClass)) #define IS_PROGRESS( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_PROGRESS )) #define IS_PROGRESS_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_PROGRESS )) #define PROGRESS_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_PROGRESS, ProgressClass )) typedef struct _Progress { iObject parent_object; /* Nest progress_begin() calls with this. */ int count; /* How long we've been busy, time since last update. */ GTimer *busy_timer; GTimer *update_timer; /* Trying to cancel. */ gboolean cancel; /* In the "busy" state, ie. we've emitted "begin" and so we need to * emit "end" on progress_end(). */ gboolean busy; /* The feedback message we suggest, percent for progress bar. */ VipsBuf feedback; char buf[PROGRESS_FEEDBACK_SIZE]; int percent; } Progress; typedef struct _ProgressClass { iObjectClass parent_class; /* Entering busy state: display progress bar, change cursor, etc. */ void (*begin)( Progress * ); /* Progress update. Set cancel to cancel computation. */ void (*update)( Progress *, gboolean *cancel ); /* End busy state. Restore screen. */ void (*end)( Progress * ); } ProgressClass; /* Called from all over nip2 as computation proceeds. */ void progress_begin( void ); gboolean progress_update_percent( int percent, int eta ); gboolean progress_update_expr( Expr *expr ); gboolean progress_update_loading( int percent, const char *filename ); gboolean progress_update_tick( void ); void progress_end( void ); GType progress_get_type( void ); Progress *progress_get( void ); ================================================ FILE: src/real.c ================================================ /* an input real ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ValueClass *parent_class = NULL; static void real_class_init( RealClass *class ) { parent_class = g_type_class_peek_parent( class ); /* Create signals. */ model_register_loadable( MODEL_CLASS( class ) ); } static void real_init( Real *real ) { iobject_set( IOBJECT( real ), CLASS_REAL, NULL ); } GType real_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( RealClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) real_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Real ), 32, /* n_preallocs */ (GInstanceInitFunc) real_init, }; type = g_type_register_static( TYPE_VALUE, "Real", &info, 0 ); } return( type ); } ================================================ FILE: src/real.h ================================================ /* a real in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_REAL (real_get_type()) #define REAL( obj ) (GTK_CHECK_CAST( (obj), TYPE_REAL, Real )) #define REAL_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_REAL, RealClass )) #define IS_REAL( obj ) (GTK_CHECK_TYPE( (obj), TYPE_REAL )) #define IS_REAL_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_REAL )) typedef struct _Real { Value parent_object; /* Private ... build iobject caption here. */ } Real; typedef struct _RealClass { ValueClass parent_class; /* My methods. */ } RealClass; GType real_get_type( void ); ================================================ FILE: src/reduce.c ================================================ /* Graph reducer. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* trace each regeneration #define DEBUG_REGEN */ /* trace each reduction #define DEBUG_TRACE */ /* trace copies of code from compile heap to main heap. #define DEBUG_COPY */ /* trace just member regeneration #define DEBUG_REGEN_MEMBER */ /* Turn on WHNF tests. #define WHNF_DEBUG */ /* regular tests that we stay in weak head normal form #define WHNF_DEBUG */ /* State of the reduction engine. */ Reduce *reduce_context; /* Index with a CombinatorType, get the number of args that combinator takes. COMB_S = 0, COMB_SL, COMB_SR, COMB_I, COMB_K, COMB_GEN, */ static int nargs[] = {3, 3, 3, 1, 2, 3}; /* Recomps this time. */ int reduce_total_recomputations = 0; /* The current expr being reduced. Used for computation feedback messages. */ static Expr *reduce_current_expr = NULL; /* Eval error here. Longjmp back a ways. */ void reduce_throw( Reduce *rc ) { if( !rc->running ) error( "panic: uncaught exception in reduce_throw()!" ); else longjmp( rc->error[--rc->running], -1 ); } static gboolean reduce_safe_pointer_wrap( Reduce *rc, PElement *out, reduce_safe_pointer_fn fn, void *a, void *b, void *c, void *d, void **result ) { REDUCE_CATCH_START( FALSE ); *result = fn( rc, out, a, b, c, d ); REDUCE_CATCH_STOP; return( TRUE ); } /* Call a function, passing in a "safe" PElement ... ie. the PElement points * at a fresh element which will be safe from the GC. */ void * reduce_safe_pointer( Reduce *rc, reduce_safe_pointer_fn fn, void *a, void *b, void *c, void *d ) { Element e; PElement pe; void *result; e.type = ELEMENT_NOVAL; e.ele = (void *) 12; PEPOINTE( &pe, &e ); heap_register_element( rc->heap, &e ); if( !reduce_safe_pointer_wrap( rc, &pe, fn, a, b, c, d, &result ) ) { heap_unregister_element( rc->heap, &e ); reduce_throw( rc ); } heap_unregister_element( rc->heap, &e ); return( result ); } void reduce_error_typecheck( Reduce *rc, PElement *e, const char *name, const char *type ) { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); error_top( _( "Typecheck error." ) ); vips_buf_appendf( &buf, _( "%s expected %s, instead saw:" ), name, type ); vips_buf_appends( &buf, "\n " ); itext_value_ev( rc, &buf, e ); error_sub( "%s", vips_buf_all( &buf ) ); reduce_throw( rc ); } static void reduce_error_toobig( Reduce *rc, const char *name ) { error_top( _( "Overflow error." ) ); error_sub( _( "%s too long." ), name ); reduce_throw( rc ); } /* 'get' a list: convert a MANAGEDSTRING into a list, if necessary. */ void reduce_get_list( Reduce *rc, PElement *list ) { if( !heap_get_list( list ) ) reduce_throw( rc ); } /* Map over a heap list. Reduce the list spine as we go, don't reduce the * heads. */ void * reduce_map_list( Reduce *rc, PElement *base, reduce_map_list_fn fn, void *a, void *b ) { PElement e = *base; reduce_spine( rc, &e ); if( !PEISLIST( &e ) ) reduce_error_typecheck( rc, &e, "reduce_map_list", "list" ); while( PEISFLIST( &e ) ) { PElement head; void *res; reduce_get_list( rc, &e ); /* Apply user function to the head. */ PEGETHD( &head, &e ); if( (res = fn( rc, &head, a, b )) ) return( res ); /* Reduce the tail. */ PEGETTL( &e, &e ); reduce_spine( rc, &e ); } return( NULL ); } typedef struct _ReduceMapDict { reduce_map_dict_fn fn; void *a; void *b; } ReduceMapDict; static void * reduce_map_dict_entry( Reduce *rc, PElement *head, ReduceMapDict *map_dict ) { char key[256]; PElement p1, p2; void *result; reduce_spine( rc, head ); if( !PEISFLIST( head ) ) reduce_error_typecheck( rc, head, "reduce_map_dict", "[*]" ); reduce_get_list( rc, head ); PEGETHD( &p1, head ); reduce_get_string( rc, &p1, key, 256 ); PEGETTL( &p2, head ); reduce_spine( rc, &p2 ); if( !PEISFLIST( &p2 ) ) reduce_error_typecheck( rc, &p2, "reduce_map_dict", "[*]" ); reduce_get_list( rc, &p2 ); PEGETHD( &p1, &p2 ); if( (result = map_dict->fn( rc, key, &p1, map_dict->a, map_dict->b )) ) return( result ); PEGETTL( &p1, &p2 ); reduce_spine( rc, &p1 ); if( !PEISELIST( &p1 ) ) reduce_error_typecheck( rc, &p1, "reduce_map_dict", "[]" ); return( NULL ); } /* Map over a list of ["key", value] pairs. */ void * reduce_map_dict( Reduce *rc, PElement *base, reduce_map_dict_fn fn, void *a, void *b ) { ReduceMapDict map_dict; map_dict.fn = fn; map_dict.a = a; map_dict.b = b; return( reduce_map_list( rc, base, (reduce_map_list_fn) reduce_map_dict_entry, &map_dict, NULL ) ); } static void * reduce_clone_list_sub( Reduce *rc, PElement *head, PElement *out ) { PElement lhs; if( !heap_list_add( rc->heap, out, &lhs ) ) reduce_throw( rc ); PEPUTPE( &lhs, head ); heap_list_next( out ); return( NULL ); } /* Clone a list ... just clone the spine, copy pointers to the heads. Reduce * the list as we go (strict shallow clone). We update out as we go, so that * on return it points to the tail (always []) of the cloned list. */ void reduce_clone_list( Reduce *rc, PElement *base, PElement *out ) { heap_list_init( out ); (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_clone_list_sub, out, NULL ); } /* Sub-fn of below. Add a character to the buffer. */ static void * reduce_add_char( Reduce *rc, PElement *base, char **buf, int *sz ) { /* Overflow? */ if( *sz == 0 ) reduce_error_toobig( rc, "[char]" ); /* Reduce this list element. */ reduce_spine( rc, base ); /* Should be a char. */ if( !PEISCHAR( base ) ) reduce_error_typecheck( rc, base, "reduce_add_char", "char" ); /* Add to buffer. */ **buf = PEGETCHAR( base ); (*buf)++; (*sz)--; return( NULL ); } /* Evaluate a PElement into a string buffer. Return the number of characters * in string, not including '\0' terminator. */ int reduce_get_string( Reduce *rc, PElement *base, char *buf, int n ) { int sz = n - 1; reduce_spine( rc, base ); if( PEISMANAGEDSTRING( base ) ) { /* A static string ... rather than expanding to a list and * parsing, we can copy directly. */ Managedstring *managedstring = PEGETMANAGEDSTRING( base ); im_strncpy( buf, managedstring->string, n ); sz -= strlen( buf ); } else { (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_add_char, &buf, &sz ); /* Add '\0' terminator. */ *buf = '\0'; } return( n - sz - 1 ); } static void * reduce_get_lstring_sub( Reduce *rc, PElement *base, GSList **labels, int *n ) { char buf[MAX_STRSIZE]; (void) reduce_get_string( rc, base, buf, MAX_STRSIZE ); *labels = g_slist_append( *labels, g_strdup( buf ) ); return( NULL ); } /* Evaluate to [[char]]. Return the number of strings we read. */ int reduce_get_lstring( Reduce *rc, PElement *base, GSList **labels ) { int n; n = 0; *labels = NULL; (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_get_lstring_sub, labels, &n ); return( n ); } /* Get an element as a boolean. */ gboolean reduce_get_bool( Reduce *rc, PElement *base ) { reduce_spine( rc, base ); if( !PEISBOOL( base ) ) reduce_error_typecheck( rc, base, "reduce_get_bool", "bool" ); return( PEGETBOOL( base ) ); } /* Get an element as a real. */ double reduce_get_real( Reduce *rc, PElement *base ) { /* Reduce this element. */ reduce_spine( rc, base ); /* Should be a real. */ if( !PEISREAL( base ) ) reduce_error_typecheck( rc, base, "reduce_get_real", "real" ); return( PEGETREAL( base ) ); } /* Get an element as a class. */ void reduce_get_class( Reduce *rc, PElement *base ) { /* Reduce this element. */ reduce_spine( rc, base ); /* Should be a class. */ if( !PEISCLASS( base ) ) reduce_error_typecheck( rc, base, "reduce_get_class", "class" ); } /* Get an element as an image. */ Imageinfo * reduce_get_image( Reduce *rc, PElement *base ) { /* Reduce this element. */ reduce_spine( rc, base ); /* Should be an image. */ if( !PEISIMAGE( base ) ) reduce_error_typecheck( rc, base, "reduce_get_image", "image" ); return( PEGETII( base ) ); } /* Sub-fn of below. Add a real to the buffer. */ static void * reduce_add_real( Reduce *rc, PElement *base, double **buf, int *sz ) { /* Overflow? */ if( *sz == 0 ) reduce_error_toobig( rc, "[real]" ); /* Add to buffer. */ **buf = reduce_get_real( rc, base ); (*buf)++; (*sz)--; return( NULL ); } /* Get an element as a realvec. Return length of vector. */ int reduce_get_realvec( Reduce *rc, PElement *base, double *buf, int n ) { int sz = n; (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_add_real, &buf, &sz ); return( n - sz ); } /* Sub-fn of below. Add an ii to the buffer. */ static void * reduce_add_image( Reduce *rc, PElement *base, Imageinfo ***buf, int *sz ) { /* Overflow? */ if( *sz == 0 ) reduce_error_toobig( rc, "[image]" ); /* Add to buffer. */ **buf = reduce_get_image( rc, base ); (*buf)++; (*sz)--; return( NULL ); } /* Get an element as a realvec. Return length of vector. */ int reduce_get_imagevec( Reduce *rc, PElement *base, Imageinfo **buf, int n ) { int sz = n; (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_add_image, &buf, &sz ); return( n - sz ); } /* Test for 1st sz elements are reals. Init sz < 0 for unlimited test. */ static void * reduce_test_real( Reduce *rc, PElement *base, int *sz ) { /* Tested enough? */ if( *sz == 0 ) return( NULL ); (void) reduce_get_real( rc, base ); (*sz)--; return( NULL ); } /* Sub fn ... get the length of a list of real. */ int reduce_get_real_size( Reduce *rc, PElement *base ) { int n; n = -1; (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_test_real, &n, NULL ); return( -1 - n ); } /* Sub fn of below ... get the length of one line from a matrix. */ static void * reduce_get_line_size( Reduce *rc, PElement *base, int *w, int *h ) { int l; l = reduce_get_real_size( rc, base ); if( *w == 0 ) *w = l; else if( *w != l ) { error_top( _( "Not rectangular." ) ); error_sub( _( "Matrix of real is not rectangular. " "Found row of length %d, should be %d." ), l, *w ); reduce_throw( rc ); } *h += 1; return( NULL ); } /* Find the size of a matrix. Write xsize/ysize to args. */ void reduce_get_matrix_size( Reduce *rc, PElement *base, int *xsize, int *ysize ) { int w, h; w = 0; h = 0; (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_get_line_size, &w, &h ); if( w == 0 || h == 0 ) { error_top( _( "Zero dimension." ) ); error_sub( _( "Matrix has width %d, height %d." ), w, h ); reduce_throw( rc ); } *xsize = w; *ysize = h; } /* Track stuff during a get_matrix in one of these. */ typedef struct { double *buf; /* Start of output buffer */ int mx; /* Size of output buffer */ int w, h; /* Size of matrix we have generated */ int i; /* Current write point */ } GetMatrixInfo; /* Sub-fn of below ... get another line of the matrix. */ static void * reduce_get_line( Reduce *rc, PElement *base, GetMatrixInfo *gmi ) { int l; int remain = gmi->mx - gmi->i; /* Read next line from matrix. */ l = reduce_get_realvec( rc, base, gmi->buf + gmi->i, remain ); /* Overflow? */ if( l > remain ) reduce_error_toobig( rc, "Matrix" ); /* 1st line? */ if( gmi->h == 0 ) gmi->w = l; else if( l != gmi->w ) { error_top( _( "Not rectangular." ) ); error_sub( _( "Matrix of real is not rectangular. " "Found row of length %d, should be %d." ), l, gmi->w ); reduce_throw( rc ); } /* Move pointers on! */ gmi->h++; gmi->i += l; return( NULL ); } /* Get an element as a matrix. Return length of buffer used. * Write xsize/ysize to args. */ int reduce_get_matrix( Reduce *rc, PElement *base, double *buf, int n, int *xsize, int *ysize ) { GetMatrixInfo gmi; gmi.buf = buf; gmi.mx = n; gmi.w = gmi.h = 0; gmi.i = 0; (void) reduce_map_list( rc, base, (reduce_map_list_fn) reduce_get_line, &gmi, NULL ); *xsize = gmi.w; *ysize = gmi.h; return( gmi.i ); } /* Test for object is the empty list. */ gboolean reduce_is_elist( Reduce *rc, PElement *base ) { reduce_spine( rc, base ); if( PEISELIST( base ) ) return( TRUE ); return( FALSE ); } /* Test for object is any list. */ gboolean reduce_is_list( Reduce *rc, PElement *base ) { reduce_spine( rc, base ); if( PEISLIST( base ) ) return( TRUE ); return( FALSE ); } /* Sub-fn of below. Test for 1st sz elements are char. We have several * possible return values :-( * * - evaluation error ... we can throw an exception * - we find a non-char in the first n elements ... return -1 * - we have tested the first n and want to stop looking ... return -2 * - all OK so far, but we want to keep looking ... return NULL */ static void * reduce_test_char( Reduce *rc, PElement *base, int *sz ) { /* Tested enough? */ if( *sz == 0 ) return( (void *) -2 ); /* Reduce this list element. */ reduce_spine( rc, base ); /* Should be a char. */ if( !PEISCHAR( base ) ) return( (void *) -1 ); /* Move on. */ (*sz)--; return( NULL ); } /* Test the first n elements of a list are char. n < 0 means test all * elements. */ static gboolean reduce_n_is_string( Reduce *rc, PElement *base, int sz ) { void *result; reduce_spine( rc, base ); /* We know managedstrings are strings without needing to expand them. */ if( PEISMANAGEDSTRING( base ) ) return( TRUE ); /* reduce_map_list() will throw an exeception if we give it a * non-list. */ if( !PEISLIST( base ) ) return( FALSE ); result = reduce_map_list( rc, base, (reduce_map_list_fn) reduce_test_char, &sz, NULL ); if( result == (void *) -1 ) return( FALSE ); return( TRUE ); } /* Test for object is string. Just test the first few elements, so we * allow infinite strings. */ gboolean reduce_is_string( Reduce *rc, PElement *base ) { return( reduce_n_is_string( rc, base, 4 ) ); } /* Test for list is a finite string. */ gboolean reduce_is_finitestring( Reduce *rc, PElement *base ) { return( reduce_n_is_string( rc, base, -1 ) ); } /* Test for list is realvec. */ gboolean reduce_is_realvec( Reduce *rc, PElement *base ) { int sz = 4; reduce_spine( rc, base ); if( !PEISLIST( base ) ) return( FALSE ); if( reduce_map_list( rc, base, (reduce_map_list_fn) reduce_test_real, &sz, NULL ) ) return( FALSE ); return( TRUE ); } /* Test for 1st sz elements are reals. Init sz < 0 for unlimited test. */ static void * reduce_test_image( Reduce *rc, PElement *base, int *sz ) { /* Tested enough? */ if( *sz == 0 ) return( NULL ); (void) reduce_get_image( rc, base ); (*sz)--; return( NULL ); } /* Test for list is imagevec. */ gboolean reduce_is_imagevec( Reduce *rc, PElement *base ) { int sz = 4; reduce_spine( rc, base ); if( !PEISLIST( base ) ) return( FALSE ); if( reduce_map_list( rc, base, (reduce_map_list_fn) reduce_test_image, &sz, NULL ) ) return( FALSE ); return( TRUE ); } /* Sub-fn of below ... test another line of the matrix. */ static void * reduce_test_line( Reduce *rc, PElement *base, int *w, int *h ) { /* Test next line from matrix. */ if( !reduce_is_realvec( rc, base ) ) return( base ); return( NULL ); } /* Test for object is [[real]] .. don't test for rectangularness. */ gboolean reduce_is_matrix( Reduce *rc, PElement *base ) { reduce_spine( rc, base ); if( !PEISLIST( base ) ) return( FALSE ); if( reduce_map_list( rc, base, (reduce_map_list_fn) reduce_test_line, NULL, NULL ) ) return( FALSE ); return( TRUE ); } /* Test for object is a class. */ gboolean reduce_is_class( Reduce *rc, PElement *klass ) { reduce_spine( rc, klass ); if( PEISCLASS( klass ) ) return( TRUE ); return( FALSE ); } /* Test for instance is an exact instance ... ie. no inheritance. FIXME ... yuk! strcmp()!! */ gboolean reduce_is_instanceof_exact( Reduce *rc, const char *name, PElement *instance ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( !reduce_is_class( rc, instance ) ) return( FALSE ); symbol_qualified_name( PEGETCLASSCOMPILE( instance )->sym, &buf ); if( strcmp( name, vips_buf_all( &buf ) ) == 0 ) return( TRUE ); return( FALSE ); } /* Test for thing is an instance of the named class symbol. */ gboolean reduce_is_instanceof( Reduce *rc, const char *name, PElement *instance ) { PElement super; reduce_spine( rc, instance ); if( !PEISCLASS( instance ) ) return( FALSE ); if( reduce_is_instanceof_exact( rc, name, instance ) ) return( TRUE ); if( class_get_super( instance, &super ) && !PEISELIST( &super ) ) return( reduce_is_instanceof( rc, name, &super ) ); return( FALSE ); } /* Find the length of a list, with a bailout for the largest size we test. * Handy for avoiding finding the length of "[1..]". */ int reduce_list_length_max( Reduce *rc, PElement *base, int max_length ) { PElement p; int i; /* Reduce to first element. */ p = *base; reduce_spine( rc, &p ); /* Does it look like the start of a list? */ if( !PEISLIST( &p ) ) reduce_error_typecheck( rc, &p, _( "List length" ), "list" ); if( PEISMANAGEDSTRING( &p ) ) { Managedstring *managedstring = PEGETMANAGEDSTRING( &p ); i = strlen( managedstring->string ); } else { /* Loop down list. */ for( i = 0; PEISFLIST( &p ); i++ ) { HeapNode *hn; if( max_length != -1 && i > max_length ) reduce_error_toobig( rc, "list" ); reduce_get_list( rc, &p ); hn = PEGETVAL( &p ); PEPOINTRIGHT( hn, &p ); reduce_spine( rc, &p ); } g_assert( PEISELIST( &p ) ); } return( i ); } /* Find the length of a list. */ int reduce_list_length( Reduce *rc, PElement *base ) { return( reduce_list_length_max( rc, base, -1 ) ); } /* Point "out" at the nth element of a list. Index from 0. */ void reduce_list_index( Reduce *rc, PElement *base, int n, PElement *out ) { PElement p; int i; HeapNode *hn; if( n < 0 ) { error_top( _( "Bad argument." ) ); error_sub( _( "List index must be positive, not %d" ), n ); reduce_throw( rc ); } p = *base; reduce_spine( rc, &p ); if( !PEISLIST( &p ) ) reduce_error_typecheck( rc, &p, _( "List index" ), "list" ); for( i = n;; ) { if( PEISELIST( &p ) ) { error_top( _( "Bad argument." ) ); error_sub( _( "List only has %d elements, " "unable to get element %d." ), n - i, n ); reduce_throw( rc ); } g_assert( PEISFLIST( &p ) ); reduce_get_list( rc, &p ); hn = PEGETVAL( &p ); PEPOINTRIGHT( hn, &p ); if( --i < 0 ) break; reduce_spine( rc, &p ); } if( trace_flags & TRACE_OPERATOR ) { VipsBuf *buf = trace_push(); trace_pelement( base ); vips_buf_appendf( buf, " \"?\" %d ->\n", n ); } PEPOINTLEFT( hn, out ); if( trace_flags & TRACE_OPERATOR ) { trace_result( TRACE_OPERATOR, out ); trace_pop(); } } /* No args allowed error. */ static void argserror( Reduce *rc, PElement *a ) { char txt[MAX_ERROR_FRAG]; VipsBuf buf = VIPS_BUF_STATIC( txt ); itext_value_ev( rc, &buf, a ); error_top( _( "No arguments allowed." ) ); error_sub( _( "Object \"%s\" should have no arguments." ), vips_buf_all( &buf ) ); reduce_throw( rc ); } #ifdef WHNF_DEBUG /* Test for PElement is in weak head-normal form. */ static gboolean is_WHNF( PElement *out ) { PElement spine; int i; HeapNode *hn; Symbol *sym; Compile *compile; int na; /* Might be a base type ... */ if( PEISREAL( out ) || PEISCOMPLEX( out ) || PEISNUM( out ) || PEISCHAR( out ) || PEISBOOL( out ) || PEISTAG( out ) || PEISIMAGE( out ) || PEISLIST( out ) || PEISCLASS( out ) || PEISSYMREF( out ) || PEISCOMPILEREF( out ) || PEISNOVAL( out ) ) return( TRUE ); /* Must be a function or generator ... loop down the spine, counting * args. */ for( spine = *out, i = 0; PEGETTYPE( &spine ) == ELEMENT_NODE; i++ ) { hn = PEGETVAL( &spine ); if( hn->type != TAG_APPL ) break; PEPOINTLEFT( PEGETVAL( &spine ), &spine ); } if( PEISBINOP( &spine ) ) { if( i > 1 ) return( FALSE ); } else if( PEISUNOP( &spine ) ) { if( i > 0 ) return( FALSE ); } else if( PEISCOMB( &spine ) ) { if( i > nargs[(int) PEGETCOMB( &spine )] - 1 ) return( FALSE ); } else if( PEISCONSTRUCTOR( &spine ) ) { compile = PEGETCOMPILE( &spine ); na = compile->nparam + compile->nsecret; if( i > na ) { printf( "constructor %s with %d args ", symbol_name( sym ), i ); printf( "should have %d args\n", compile->nparam ); return( FALSE ); } } else if( PEISSYMBOL( &spine ) ) { /* If it's a VIPS or a builtin with too few args, it's OK. */ sym = SYMBOL( PEGETVAL( &spine ) ); if( sym->type == SYM_EXTERNAL ) { if( i < sym->fn_nargs ) return( TRUE ); } else if( sym->type == SYM_BUILTIN ) { if( i < sym->builtin->nargs ) return( TRUE ); } /* Nope ... should have been reduced. */ return( FALSE ); } else { return( FALSE ); } return( TRUE ); } #endif /*WHNF_DEBUG*/ /* Main reduction machine loop. */ void reduce_spine( Reduce *rc, PElement *out ) { Heap *heap = rc->heap; PElement np; /* Check for possible C stack overflow ... can't go over 2M on most * systems if we're using (or any of our libs are using) threads. */ if( (char *) main_c_stack_base - (char *) &rc > 2000000 ) { error_top( _( "Overflow error." ) ); error_sub( _( "C stack overflow. Expression too complex." ) ); reduce_throw( rc ); } /* Point node pointer at reduction start. */ np = *out; /* Start a new frame. */ RSPUSHFRAME( rc, out ); reduce_start: reduce_total_recomputations += 1; if( (reduce_total_recomputations % 100000) == 0 ) { if( progress_update_expr( reduce_current_expr ) ) { error_top( _( "Cancelled." ) ); error_sub( _( "Evaluation cancelled." ) ); reduce_throw( rc ); } } #ifdef DEBUG_TRACE { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( rc->heap, &buf, out, TRUE ); printf( "reduce_spine: %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_TRACE*/ switch( PEGETTYPE( &np ) ) { case ELEMENT_CHAR: case ELEMENT_BOOL: case ELEMENT_ELIST: case ELEMENT_TAG: case ELEMENT_SYMREF: case ELEMENT_COMPILEREF: case ELEMENT_MANAGED: /* Base type .. no more reduction needed. */ /* Should have no args. */ if( RSFRAMESIZE( rc ) != 0 ) argserror( rc, &np ); break; case ELEMENT_CONSTRUCTOR: { Compile *compile; HeapNode **arg; PElement rhs1; int na; /* Class constructor. */ compile = PEGETCOMPILE( &np ); g_assert( is_class( compile ) ); /* Class args ... real params, secret params. */ na = compile->nparam + compile->nsecret; /* Get args. */ if( !RSCHECKARGS( rc, na ) ) break; arg = &RSGET( rc, na - 1 ); if( na == 0 ) { /* Zero args ... just construct on top of the current * node pointer. */ action_proc_construct( rc, compile, arg, &np ); goto reduce_start; } /* Overwrite RHS of arg[0], make LHS into COMB_I. */ PEPOINTRIGHT( arg[0], &rhs1 ); action_proc_construct( rc, compile, arg, &rhs1 ); PPUTLEFT( arg[0], ELEMENT_COMB, COMB_I ); RSPOP( rc, na ); if( RSFRAMEEMPTY( rc ) ) np = RSGETWB( rc ); else PEPOINTLEFT( RSGET( rc, 0 ), &np ); PEPUTP( &np, GETRT( arg[0] ), GETRIGHT( arg[0] ) ); goto reduce_start; } case ELEMENT_SYMBOL: { Symbol *sym = PEGETSYMBOL( &np ); g_assert( sym ); switch( sym->type ) { case SYM_VALUE: { Compile *compile = sym->expr->compile; /* Make sure it's clean ... we can get * links to dirty syms through dynamic dependencies. */ if( sym->dirty ) { error_top( _( "No value." ) ); error_sub( _( "Symbol \"%s\" has no value." ), symbol_name( sym ) ); reduce_throw( rc ); } /* We copy code, but link to values. We have to take a * fresh copy of code as (together with any args our * context might supply) it will expand to a value, * which we might then edit in a row. We want to make * sure any edits do not zap the original code. */ if( compile->nparam + compile->nsecret == 0 ) { /* Make sure the value has copied to the main * heap. */ if( PEISNOVAL( &sym->expr->root ) ) { gboolean res; res = reduce_regenerate( sym->expr, &sym->expr->root ); expr_new_value( sym->expr ); if( !res ) reduce_throw( rc ); } /* Link to this sym's value. */ PEPUTPE( &np, &sym->expr->root ); } else /* Copy compiled code from the private compile * heap. */ if( !heap_copy( rc->heap, compile, &np ) ) reduce_throw( rc ); goto reduce_start; } case SYM_PARAM: /* All params should be taken out by var abstract. */ printf( "sym-param found, argh: " ); symbol_name_print( sym ); printf( "\n" ); g_assert( FALSE ); break; case SYM_EXTERNAL: { HeapNode **arg; int na; /* A VIPS function. */ na = sym->fn_nargs; /* Get args. */ if( !RSCHECKARGS( rc, na ) ) /* Not enough ... function result. */ break; /* Run strictly. */ arg = &RSGET( rc, na - 1 ); action_dispatch( rc, NULL, reduce_spine, -1, sym->function->name, FALSE, (ActionFn) call_run, na, arg, sym->function ); /* Find output element. */ RSPOP( rc, na ); if( RSFRAMEEMPTY( rc ) ) np = RSGETWB( rc ); else PEPOINTLEFT( RSGET( rc, 0 ), &np ); /* Write to node above. */ PEPUTP( &np, GETRT( arg[0] ), GETRIGHT( arg[0] ) ); goto reduce_start; } case SYM_BUILTIN: { HeapNode **arg; int na; /* A builtin function. */ na = sym->builtin->nargs; /* Get args. */ if( !RSCHECKARGS( rc, na ) ) /* Not enough ... function result. */ break; /* Run strictly. */ arg = &RSGET( rc, na - 1 ); action_dispatch( rc, NULL, reduce_spine, -1, sym->builtin->name, sym->builtin->override, (ActionFn) builtin_run, na, arg, sym->builtin ); /* Find output element. */ RSPOP( rc, na ); if( RSFRAMEEMPTY( rc ) ) np = RSGETWB( rc ); else PEPOINTLEFT( RSGET( rc, 0 ), &np ); /* Write to node above. */ PEPUTP( &np, GETRT( arg[0] ), GETRIGHT( arg[0] ) ); goto reduce_start; } case SYM_ZOMBIE: { Symbol *new_sym; /* Could be defined on an enclosing scope. Search * outwards for a definition. */ if( !(new_sym = compile_resolve_top( sym )) ) { symbol_not_defined( sym ); reduce_throw( rc ); } /* Zap linked symbol into graph. */ PEPUTP( &np, ELEMENT_SYMBOL, new_sym ); goto reduce_start; } case SYM_ROOT: case SYM_WORKSPACE: case SYM_WORKSPACEROOT: /* Becomes a symref ... base type. */ PEPUTP( &np, ELEMENT_SYMREF, sym ); /* Should have no args. */ if( RSFRAMESIZE( rc ) != 0 ) argserror( rc, &np ); break; default: g_assert( FALSE ); } break; } case ELEMENT_NODE: { HeapNode *hn; /* Get the node that np points to. */ hn = PEGETVAL( &np ); switch( hn->type ) { case TAG_CONS: case TAG_DOUBLE: case TAG_COMPLEX: case TAG_CLASS: /* Base type ... reduction all done! We don't test * that class's superclasses are base, as they aren't * always for non-top-level base types ... see * reduce_pelement(). */ /* Should have no args. */ if( RSFRAMESIZE( rc ) != 0 ) argserror( rc, &np ); break; case TAG_APPL: /* Function application ... push this node and loop * down the LHS looking for a combinator. */ /* Push this node. */ RSPUSH( rc, hn ); /* Move down left branch. */ PEPOINTLEFT( hn, &np ); goto reduce_start; case TAG_GEN: { double d1; double d2; double d3 = 0.0; /* keeps gcc happy */ gboolean limit; HeapNode *hn1, *hn2; /* Extract next, step, final. */ d1 = GETLEFT( hn )->body.num; d2 = GETLEFT( GETRIGHT( hn ) )->body.num; limit = GETRT( GETRIGHT( hn ) ) != ELEMENT_ELIST; if( limit ) d3 = GETRIGHT( GETRIGHT( hn ) )->body.num; if( trace_flags & TRACE_OPERATOR ) { VipsBuf *buf = trace_push(); if( limit ) vips_buf_appendf( buf, "generator %g %g %g ->\n", d1, d2, d3 ); else vips_buf_appendf( buf, "generator %g %g ->\n", d1, d2 ); } /* At end? */ if( GETRT( GETRIGHT( hn ) ) != ELEMENT_ELIST && ((d2 > 0 && d1 > d3) || (d2 < 0 && d1 < d3)) ) { /* Make I node for end. */ hn->type = TAG_APPL; PPUT( hn, ELEMENT_COMB, COMB_I, ELEMENT_ELIST, NULL ); /* Write back to node above. */ PEPUTP( &np, ELEMENT_ELIST, NULL ); if( trace_flags & TRACE_OPERATOR ) { trace_result( TRACE_OPERATOR, &np ); trace_pop(); } /* All done! */ break; } /* Not at end, or no final. Generate new gen node. */ if( NEWNODE( heap, hn1 ) ) reduce_throw( rc ); *hn1 = *hn; /* Change hn into CONS node. */ hn->type = TAG_CONS; PPUTRIGHT( hn, ELEMENT_NODE, hn1 ); /* Generate new number. */ if( NEWNODE( heap, hn2 ) ) reduce_throw( rc ); hn2->type = TAG_DOUBLE; hn2->body.num = d1 + d2; PPUTLEFT( hn1, ELEMENT_NODE, hn2 ); if( trace_flags & TRACE_OPERATOR ) { trace_result( TRACE_OPERATOR, &np ); trace_pop(); } /* And loop! */ goto reduce_start; } case TAG_FILE: { Managedfile *managedfile = MANAGEDFILE( GETLEFT( hn ) ); int ch = managedfile_getc( managedfile ); /* -1 means error, 0 means EOF. */ if( ch == -1 ) reduce_throw( rc ); else if( ch == 0 ) { /* Turn us into []. */ hn->type = TAG_APPL; PPUT( hn, ELEMENT_COMB, COMB_I, ELEMENT_ELIST, NULL ); } else { HeapNode *hn1; /* Not at end ... make another CONS. */ if( NEWNODE( heap, hn1 ) ) reduce_throw( rc ); *hn1 = *hn; hn->type = TAG_CONS; PPUT( hn, ELEMENT_CHAR, GUINT_TO_POINTER( ch ), ELEMENT_NODE, hn1 ); } /* Loop again with new np. */ goto reduce_start; } case TAG_FREE: g_assert( FALSE ); default: g_assert( FALSE ); } break; } case ELEMENT_COMB: { CombinatorType comb = PEGETCOMB( &np ); HeapNode *hn1, *hn2; HeapNode **arg; int na; na = nargs[(int) comb]; /* Get args. */ if( !RSCHECKARGS( rc, na ) ) /* Not enough ... function result. */ break; /* Extract args. */ arg = &RSGET( rc, na - 1 ); switch( comb ) { case COMB_S: /* Rewrite graph for S a b c => (a c) (b c). */ /* Make (b c) appl node. */ if( NEWNODE( heap, hn1 ) ) reduce_throw( rc ); *hn1 = *arg[0]; PPUTLEFT( hn1, GETRT( arg[1] ), GETRIGHT( arg[1] ) ); PPUTRIGHT( arg[0], ELEMENT_NODE, hn1 ); /* Make (a c) appl node. */ if( NEWNODE( heap, hn2 ) ) reduce_throw( rc ); *hn2 = *hn1; PPUTLEFT( hn2, GETRT( arg[2] ), GETRIGHT( arg[2] ) ); PPUTLEFT( arg[0], ELEMENT_NODE, hn2 ); /* End of S ... now pop three, push 1 and loop. */ RSPOP( rc, 2 ); PEPOINTLEFT( arg[0], &np ); goto reduce_start; case COMB_SL: /* Rewrite graph for Sl a b c => (a c) b. */ /* Make (a c) appl node. */ if( NEWNODE( heap, hn1 ) ) reduce_throw( rc ); *hn1 = *arg[0]; PPUTLEFT( hn1, GETRT( arg[2] ), GETRIGHT( arg[2] ) ); PPUT( arg[0], ELEMENT_NODE, hn1, GETRT( arg[1] ), GETRIGHT( arg[1] ) ); /* End of SL ... now pop three, push 1 and loop. */ RSPOP( rc, 2 ); PEPOINTLEFT( arg[0], &np ); goto reduce_start; case COMB_SR: /* Rewrite graph for Sr a b c => a (b c). */ /* Make (b c) appl node. */ if( NEWNODE( heap, hn1 ) ) reduce_throw( rc ); *hn1 = *arg[0]; PPUTLEFT( hn1, GETRT( arg[1] ), GETRIGHT( arg[1] ) ); PPUT( arg[0], GETRT( arg[2] ), GETRIGHT( arg[2] ), ELEMENT_NODE, hn1 ); /* End of SR ... now pop three, push 1 and loop. */ RSPOP( rc, 2 ); PEPOINTLEFT( arg[0], &np ); goto reduce_start; case COMB_I: /* No action necessary. */ break; case COMB_K: /* Make I node. */ PPUT( arg[0], ELEMENT_COMB, COMB_I, GETRT( arg[1] ), GETRIGHT( arg[1] ) ); break; case COMB_GEN: { double d1; double d2 = 0.0; /* Don't need to init, but */ double d3 = 0.0; /* keeps gcc happy */ PElement rhs1, rhs2, rhs3; PEPOINTRIGHT( arg[2], &rhs1 ); PEPOINTRIGHT( arg[1], &rhs2 ); PEPOINTRIGHT( arg[0], &rhs3 ); reduce_spine_strict( rc, &rhs1 ); reduce_spine_strict( rc, &rhs2 ); reduce_spine_strict( rc, &rhs3 ); /* May have done ourselves in the process. */ if( arg[0]->type != TAG_APPL ) break; /* Typecheck. */ if( !PEISREAL( &rhs1 ) ) reduce_error_typecheck( rc, &rhs1, _( "List generator" ), "real" ); d1 = PEGETREAL( &rhs1 ); if( !PEISELIST( &rhs2 ) && !PEISREAL( &rhs2 ) ) reduce_error_typecheck( rc, &rhs2, _( "List generator" ), "real" ); if( PEISREAL( &rhs2 ) ) d2 = PEGETREAL( &rhs2 ); if( !PEISELIST( &rhs3 ) && !PEISREAL( &rhs3 ) ) reduce_error_typecheck( rc, &rhs3, _( "List generator" ), "real" ); if( PEISREAL( &rhs3 ) ) d3 = PEGETREAL( &rhs3 ); if( trace_flags & TRACE_OPERATOR ) { VipsBuf *buf = trace_push(); vips_buf_appends( buf, "generator constructor " ); trace_args( arg, 3 ); } /* If next is missing, set default. */ if( PEISREAL( &rhs2 ) ) /* Next is there, calculate step. */ d2 = d2 - d1; else { /* If final is missing, default is 1. */ if( PEISELIST( &rhs3 ) ) d2 = 1; else { /* Final is there, choose 1 or -1. */ if( d1 < d3 ) d2 = 1; else d2 = -1; } } /* Make node for pairing next and final fields. */ if( NEWNODE( heap, hn1 ) ) reduce_throw( rc ); hn1->type = TAG_COMPLEX; PPUT( hn1, GETRT( arg[1] ), GETRIGHT( arg[1] ), GETRT( arg[0] ), GETRIGHT( arg[0] ) ); /* Link to old root, make gen node. */ arg[0]->type = TAG_GEN; PPUT( arg[0], GETRT( arg[2] ), GETRIGHT( arg[2] ), ELEMENT_NODE, hn1 ); /* Make step node. */ if( NEWNODE( heap, hn2 ) ) reduce_throw( rc ); hn2->type = TAG_DOUBLE; hn2->body.num = d2; PPUTLEFT( hn1, ELEMENT_NODE, hn2 ); if( trace_flags & TRACE_OPERATOR ) { VipsBuf *buf = trace_current(); vips_buf_appends( buf, " " ); trace_node( arg[0] ); vips_buf_appends( buf, "\n" ); trace_text( TRACE_OPERATOR, "%s", vips_buf_all( buf ) ); trace_pop(); } /* Find output element. */ RSPOP( rc, 3 ); if( RSFRAMEEMPTY( rc ) ) np = RSGETWB( rc ); else PEPOINTLEFT( RSGET( rc, 0 ), &np ); /* Restart from there. */ goto reduce_start; } default: g_assert( FALSE ); } /* Find output element. */ RSPOP( rc, na ); if( RSFRAMEEMPTY( rc ) ) np = RSGETWB( rc ); else PEPOINTLEFT( RSGET( rc, 0 ), &np ); /* Write to above node. */ PEPUTP( &np, GETRT( arg[0] ), GETRIGHT( arg[0] ) ); /* Loop again with new np. */ goto reduce_start; /*NOTREACHED*/ } case ELEMENT_BINOP: { BinOp bop = PEGETBINOP( &np ); HeapNode **arg; Compile *compile; PElement rhs1, rhs2; /* Three args to binops ... first is the Compile that built us * (for error messages), other two are actual args. */ if( !RSCHECKARGS( rc, 3 ) ) /* Not enough ... function result. */ break; /* Extract args. */ arg = &RSGET( rc, 2 ); compile = COMPILE( GETRIGHT( arg[2] ) ); /* CONS is very, very lazy ... more like a combinator. */ if( bop == BI_CONS ) { PEPOINTRIGHT( arg[1], &rhs1 ); if( trace_flags & TRACE_OPERATOR ) { trace_push(); PEPOINTRIGHT( arg[0], &rhs2 ); trace_binop( compile, &rhs1, bop, &rhs2 ); } arg[0]->type = TAG_CONS; PPUTLEFT( arg[0], PEGETTYPE( &rhs1 ), PEGETVAL( &rhs1 ) ); if( trace_flags & TRACE_OPERATOR ) { VipsBuf *buf = trace_current(); vips_buf_appends( buf, " " ); trace_node( arg[0] ); vips_buf_appends( buf, "\n" ); trace_text( TRACE_OPERATOR, "%s", vips_buf_all( buf ) ); trace_pop(); } RSPOP( rc, 3 ); break; } action_proc_bop( rc, compile, bop, arg ); /* Find output element. */ RSPOP( rc, 3 ); if( RSFRAMEEMPTY( rc ) ) np = RSGETWB( rc ); else PEPOINTLEFT( RSGET( rc, 0 ), &np ); /* Write to node above. */ PEPUTP( &np, GETRT( arg[0] ), GETRIGHT( arg[0] ) ); /* Loop again with new np. */ goto reduce_start; } case ELEMENT_UNOP: { HeapNode **arg; Compile *compile; /* Some unary operator. First arg is the compile that built * us, 2nd is the actual arg that might need reducing. */ if( !RSCHECKARGS( rc, 2 ) ) /* Not enough ... function result. */ break; /* Extract arg. */ arg = &RSGET( rc, 1 ); compile = COMPILE( GETRIGHT( arg[1] ) ); action_dispatch( rc, compile, reduce_spine, PEGETUNOP( &np ), OPERATOR_NAME( PEGETUNOP( &np ) ), TRUE, (ActionFn) action_proc_uop, 1, arg, NULL ); /* Find output element. */ RSPOP( rc, 2 ); if( RSFRAMEEMPTY( rc ) ) np = RSGETWB( rc ); else PEPOINTLEFT( RSGET( rc, 0 ), &np ); /* Write to above node. */ PEPUTP( &np, GETRT( arg[0] ), GETRIGHT( arg[0] ) ); /* Loop again with new np. */ goto reduce_start; } case ELEMENT_NOVAL: break; default: g_assert( FALSE ); } /* Unwind stack, restore frame pointer. */ RSPOPFRAME( rc ); #ifdef WHNF_DEBUG /* Should now be in WHNF ... test! */ if( !is_WHNF( out ) ) { char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, out, TRUE ); printf( "*** internal error:\n" ); printf( "result of reduce_spine not in WHNF: " ); printf( "%s\n", vips_buf_all( &buf ) ); reduce_throw( rc ); } #endif /*WHNF_DEBUG*/ } /* Strict reduction ... fully eval all lists etc. */ void reduce_spine_strict( Reduce *rc, PElement *np ) { PElement rhs, lhs; /* Make sure this element is reduced. */ reduce_spine( rc, np ); /* If it's a non-empty list, may need to reduce inside. Not managed * strings though, we can leave them unevaluated. */ if( PEISFLIST( np ) && !PEISMANAGEDSTRING( np ) ) { /* Recurse for head and tail. */ HeapNode *hn = PEGETVAL( np ); PEPOINTLEFT( hn, &lhs ); PEPOINTRIGHT( hn, &rhs ); reduce_spine_strict( rc, &lhs ); reduce_spine_strict( rc, &rhs ); } } /* Free a Reduce. */ void reduce_destroy( Reduce *rc ) { heap_unregister_reduce( rc->heap, rc ); UNREF( rc->heap ); IM_FREE( rc ); } /* Max cells function for main reduce engine. Read from Preferences, and scale * by the number of workspaces we have open. */ static int reduce_heap_max_fn( Heap *heap ) { return( workspace_number() * MAX_HEAPSIZE ); } /* Build a Reduce. */ Reduce * reduce_new( void ) { /* Initial heap size. Big enough that we won't need to grow just * loading prefs and standard stuff. */ const int stsz = 100000; /* Heap increment.. */ const int incr = 2000; Reduce *rc = INEW( NULL, Reduce ); if( !rc ) return( NULL ); rc->sp = 0; rc->fsp = 0; rc->heap = NULL; rc->running = 0; rc->heap = heap_new( NULL, reduce_heap_max_fn, stsz, incr ); g_object_ref( G_OBJECT( rc->heap ) ); iobject_sink( IOBJECT( rc->heap ) ); heap_register_reduce( rc->heap, rc ); iobject_set( IOBJECT( rc->heap ), "reduce-heap", NULL ); return( rc ); } /* Reduce a PElement to a base type. Return TRUE/FALSE, no longjmp. */ gboolean reduce_pelement( Reduce *rc, ReduceFunction fn, PElement *out ) { gboolean res = TRUE; REDUCE_CATCH_START( FALSE ); fn( reduce_context, out ); REDUCE_CATCH_STOP; return( res ); } /* Make sure a symbol's value is registered with the main GC. */ void reduce_register( Symbol *sym ) { Reduce *rc = reduce_context; Heap *heap = rc->heap; heap_register_element( heap, &sym->base ); } /* Make sure a symbol's value is not registered with the main GC. */ void reduce_unregister( Symbol *sym ) { Reduce *rc = reduce_context; Heap *heap = rc->heap; heap_unregister_element( heap, &sym->base ); } /* Copy and evaluate compiled code into element pointed to by out. */ gboolean reduce_regenerate( Expr *expr, PElement *out ) { Reduce *rc = reduce_context; Heap *heap = rc->heap; /* Clear any run state from old expr value. */ expr_error_clear( expr ); if( slist_map( expr->dynamic_links, (SListMapFn) link_expr_destroy, NULL ) ) return( FALSE ); /* Copy new code in. */ if( !heap_copy( heap, expr->compile, out ) ) { expr_error_set( expr ); return( FALSE ); } #ifdef DEBUG_REGEN { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, out, TRUE ); printf( "reduce_regenerate: reducing " ); expr_name_print( expr ); printf( "graph: %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_REGEN*/ reduce_current_expr = expr; if( !reduce_pelement( rc, reduce_spine, out ) ) { reduce_current_expr = NULL; expr_error_set( expr ); (void) heap_gc( heap ); return( FALSE ); } reduce_current_expr = NULL; #ifdef DEBUG_REGEN { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); /* Force immediate GC to pick up any stray pointers. */ if( !heap_gc( heap ) ) { expr_error_set( expr ); return( FALSE ); } graph_pelement( heap, &buf, out, TRUE ); printf( "reduce_regenerate: reduced " ); expr_name_print( expr ); printf( " to: %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_REGEN*/ return( TRUE ); } /* Regenerate an (expr this) pair. */ gboolean reduce_regenerate_member( Expr *expr, PElement *ths, PElement *out ) { Reduce *rc = reduce_context; Heap *heap = rc->heap; PElement e; HeapNode *apl; /* New (NULL this) pair. */ if( NEWNODE( heap, apl ) ) { expr_error_set( expr ); return( FALSE ); } apl->type = TAG_APPL; PPUT( apl, ELEMENT_NOVAL, (void *) 10, PEGETTYPE( ths ), PEGETVAL( ths ) ); PEPUTP( out, ELEMENT_NODE, apl ); /* Link code to node. */ PEPOINTLEFT( apl, &e ); if( !reduce_regenerate( expr, &e ) ) return( FALSE ); #ifdef DEBUG_REGEN_MEMBER { char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); graph_pelement( heap, &buf, out, TRUE ); printf( "reduce_regenerate_member: " ); expr_name_print( expr ); printf( " new code: %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG_REGEN_MEMBER*/ /* Do initial reduction. */ if( !reduce_pelement( rc, reduce_spine, out ) ) { /* Failure! Junk the half-made value. */ expr_error_set( expr ); (void) heap_gc( heap ); return( FALSE ); } /* Special case: if this is a "super" row, we need to rebuild the * class. */ if( is_super( expr->compile->sym ) ) { Compile *parent = compile_get_parent( expr->compile ); PElement instance; PEPOINTE( &instance, &expr->row->scol->base ); if( !class_new_super( heap, parent, ths, &instance ) ) return( FALSE ); } return( TRUE ); } ================================================ FILE: src/reduce.h ================================================ /* Header for reduction machine. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Huge :-( this pushes sizeof(Reduce) up to 14MB. But we need to be able to * loop down very long lists, eg. for a 65k x 3 LUT held as a Matrix. Drop * this down when we represent matricies more sensibly. */ #define SPINE_SIZE (80000) /* Reduction machine state. Not very opaque ... see mark_reduce() */ struct _Reduce { /* Stack of heap nodes for spine. */ HeapNode *nstack[SPINE_SIZE]; /* Index of free element above node stack top. */ int sp; /* Frame stack ... top of fstack is sp we block GET above. */ int fstack[SPINE_SIZE]; /* Writeback stack ... where the result of each frame goes. */ PElement wbstack[SPINE_SIZE]; /* Frame stack pointer. */ int fsp; /* Heap we evaluate. */ Heap *heap; /* Nested reductions ... need to be able to longjmp() out of stuff, * and restore the machine state. */ int running; jmp_buf error[SPINE_SIZE]; int sps[SPINE_SIZE]; int fsps[SPINE_SIZE]; int tsp[SPINE_SIZE]; }; #define RSPUSH(RC,N) { \ if( (RC)->sp == SPINE_SIZE ) { \ error_top( _( "Stack overflow." ) ); \ error_sub( _( "Spine stack overflow, runaway recursion?" ) ); \ reduce_throw( (RC) ); \ } \ else \ (RC)->nstack[(RC)->sp++]=(N); \ } /* Number of items in current frame. */ #define RSFRAMESIZE(RC) ((RC)->sp - (RC)->fstack[(RC)->fsp - 1]) /* Check for at least N args present. */ #define RSCHECKARGS(RC,N) (RSFRAMESIZE(RC) >= (N)) /* Frame is empty? */ #define RSFRAMEEMPTY(RC) (RSFRAMESIZE(RC) == 0) /* Get offset from stack top, offset 0 is top item. */ #define RSGET(RC,N) ((RC)->nstack[(RC)->sp - ((N) + 1)]) /* Get the writeback for this frame. */ #define RSGETWB(RC) ((RC)->wbstack[(RC)->fsp - 1]) #define RSPUSHFRAME(RC,OUT) { \ if( (RC)->fsp == SPINE_SIZE ) { \ error_top( _( "Stack overflow." ) ); \ error_sub( _( "Frame stack overflow, " \ "expression too complex." ) ); \ reduce_throw( (RC) ); \ } \ else { \ (RC)->wbstack[(RC)->fsp] = *out; \ (RC)->fstack[(RC)->fsp] = (RC)->sp; \ (RC)->fsp++; \ } \ } #define RSPOPFRAME(RC) { \ if( (RC)->fsp == 0 ) { \ error_top( _( "Stack underflow." ) ); \ error_sub( _( "Frame stack underflow, you've found a bug!" ) ); \ reduce_throw( (RC) ); \ } \ else { \ (RC)->fsp--; \ (RC)->sp = (RC)->fstack[(RC)->fsp]; \ } \ } #define RSPOP(RC,N) { \ if( !RSCHECKARGS(RC,N) ) { \ error_top( _( "Stack underflow." ) ); \ error_sub( _( "Spine stack underflow, you've found a bug!" ) ); \ reduce_throw( (RC) ); \ } \ else \ (RC)->sp -= (N); \ } /* Pop this code before any calls to reduce_*() to init stuff and catch * errors. Arg is function return value. The missing running decrement is done * by throw(). */ #define REDUCE_CATCH_START( R ) \ { \ rc->sps[rc->running] = rc->sp; \ rc->fsps[rc->running] = rc->fsp; \ rc->tsp[rc->running] = trace_get_mark(); \ if( setjmp( rc->error[rc->running++] ) ) { \ g_assert( rc->running >= 0 ); \ rc->sp = rc->sps[rc->running]; \ rc->fsp = rc->fsps[rc->running]; \ trace_pop_to( rc->tsp[rc->running] ); \ return( (R) ); \ } \ } /* After any calls to reduce_*(). */ #define REDUCE_CATCH_STOP \ { \ rc->running -= 1; \ g_assert( rc->running >= 0 ); \ } /* Util. */ void reduce_throw( Reduce *rc ) __attribute__((noreturn)); typedef void *(*reduce_safe_pointer_fn)( Reduce *rc, PElement *, void *, void *, void *, void * ); void *reduce_safe_pointer( Reduce *rc, reduce_safe_pointer_fn fn, void *a, void *b, void *c, void *d ); void reduce_get_list( Reduce *rc, PElement *list ); void reduce_error_typecheck( Reduce *rc, PElement *e, const char *name, const char *type ); typedef void *(*reduce_map_list_fn)( Reduce *rc, PElement *, void *, void * ); void *reduce_map_list( Reduce *rc, PElement *base, reduce_map_list_fn fn, void *a, void *b ); typedef void *(*reduce_map_dict_fn)( Reduce *, const char *, PElement *, void *a, void *b ); void *reduce_map_dict( Reduce *rc, PElement *base, reduce_map_dict_fn fn, void *a, void *b ); void reduce_clone_list( Reduce *rc, PElement *base, PElement *out ); int reduce_get_string( Reduce *rc, PElement *base, char *buf, int n ); int reduce_get_lstring( Reduce *rc, PElement *base, GSList **labels ); gboolean reduce_get_bool( Reduce *rc, PElement *base ); double reduce_get_real( Reduce *rc, PElement *base ); void reduce_get_class( Reduce *rc, PElement *base ); Imageinfo *reduce_get_image( Reduce *rc, PElement *base ); int reduce_get_realvec( Reduce *rc, PElement *base, double *buf, int n ); int reduce_get_imagevec( Reduce *rc, PElement *base, Imageinfo **buf, int n ); int reduce_get_matrix( Reduce *rc, PElement *base, double *buf, int n, int *xsize, int *ysize ); void reduce_get_matrix_size( Reduce *rc, PElement *base, int *xsize, int *ysize ); gboolean reduce_is_elist( Reduce *rc, PElement *base ); gboolean reduce_is_list( Reduce *rc, PElement *base ); gboolean reduce_is_string( Reduce *rc, PElement *base ); gboolean reduce_is_finitestring( Reduce *rc, PElement *base ); gboolean reduce_is_realvec( Reduce *rc, PElement *base ); gboolean reduce_is_imagevec( Reduce *rc, PElement *base ); gboolean reduce_is_matrix( Reduce *rc, PElement *base ); gboolean reduce_is_class( Reduce *rc, PElement *klass ); int reduce_list_length( Reduce *rc, PElement *base ); int reduce_list_length_max( Reduce *rc, PElement *base, int max_length ); void reduce_list_index( Reduce *rc, PElement *base, int n, PElement *out ); gboolean reduce_is_instanceof_exact( Reduce *rc, const char *name, PElement *instance ); gboolean reduce_is_instanceof( Reduce *rc, const char *name, PElement *instance ); /* Main. */ extern Reduce *reduce_context; extern int reduce_total_recomputations; void reduce_destroy( Reduce *rc ); Reduce *reduce_new( void ); gboolean reduce_regenerate( Expr *expr, PElement *out ); gboolean reduce_regenerate_member( Expr *expr, PElement *ths, PElement *out ); void reduce_spine( Reduce *rc, PElement *out ); void reduce_spine_strict( Reduce *rc, PElement *out ); gboolean reduce_pelement( Reduce *, ReduceFunction fn, PElement *out ); /* Register and unregister values. */ void reduce_register( Symbol *sym ); void reduce_unregister( Symbol *sym ); ================================================ FILE: src/regionview.c ================================================ /* run the displays for regions on images */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Verbose. #define DEBUG */ /* Just trace create/destroy. #define DEBUG_MAKE */ /* Trace grab/ungrab #define DEBUG_GRAB */ /* Define this to trace event propogation #define EVENT */ /* See paint events. #define DEBUG_PAINT */ /* Define this to make region drags default to no-update during drag/resize. #define NO_UPDATE */ #include "ip.h" typedef void *(*regionview_rect_fn)( Regionview *, Rect *, void * ); typedef void (*regionview_paint_fn)( Regionview * ); /* Cursor shape for each resize type. */ iWindowShape regionview_cursors[REGIONVIEW_RESIZE_LAST] = { IWINDOW_SHAPE_EDIT, /* REGIONVIEW_RESIZE_NONE */ IWINDOW_SHAPE_MOVE, /* REGIONVIEW_RESIZE_MOVE */ IWINDOW_SHAPE_MOVE, /* REGIONVIEW_RESIZE_EDIT */ IWINDOW_SHAPE_TOPLEFT, /* REGIONVIEW_RESIZE_TOPLEFT */ IWINDOW_SHAPE_TOP, /* REGIONVIEW_RESIZE_TOP */ IWINDOW_SHAPE_TOPRIGHT, /* REGIONVIEW_RESIZE_TOPRIGHT */ IWINDOW_SHAPE_RIGHT, /* REGIONVIEW_RESIZE_RIGHT */ IWINDOW_SHAPE_BOTTOMRIGHT, /* REGIONVIEW_RESIZE_BOTTOMRIGHT */ IWINDOW_SHAPE_BOTTOM, /* REGIONVIEW_RESIZE_BOTTOM */ IWINDOW_SHAPE_BOTTOMLEFT, /* REGIONVIEW_RESIZE_BOTTOMLEFT */ IWINDOW_SHAPE_LEFT /* REGIONVIEW_RESIZE_LEFT */ }; /* Region border width, without shadows. */ static const int regionview_border_width = 2; /* Space around text in label. */ static const int regionview_label_border = 5; /* Length of crosshair bars. */ static const int regionview_crosshair_length = 5; /* The center of the crosshair is also sensitive for arrows. */ static const int regionview_crosshair_centre = 8; /* How close you need to get to switch the type. */ static const int regionview_morph_threshold = 20; static ViewClass *parent_class = NULL; /* Just one popup for all regions. */ static GtkWidget *regionview_popup_menu = NULL; /* Paint a rectangle. */ void regionview_paint_rect( GdkDrawable *draw, GdkGC *gc, Rect *r ) { gdk_draw_rectangle( draw, gc, FALSE, r->left, r->top, IM_MAX( 0, r->width - 1 ), IM_MAX( 0, r->height - 1 ) ); } /* Paint a thick rectangle. */ void regionview_paint_rect_thick( GdkDrawable *draw, GdkGC *gc, Rect *r, int n ) { Rect our_r; int i; our_r = *r; for( i = 0; i < n; i++ ) { regionview_paint_rect( draw, gc, &our_r ); im_rect_marginadjust( &our_r, 1 ); } } /* Paint a rect in 3D --- pass a GC for the top-left and a gc for the * bottom-right shadows. */ static void regionview_paint_rect_3d( GdkDrawable *draw, GdkGC *tl, GdkGC *br, Rect *r ) { /* Bottom and right. */ gdk_draw_line( draw, br, IM_RECT_RIGHT( r ) - 1, r->top, IM_RECT_RIGHT( r ) - 1, IM_RECT_BOTTOM( r ) - 1 ); gdk_draw_line( draw, br, IM_RECT_RIGHT( r ) - 1, IM_RECT_BOTTOM( r ) - 1, r->left, IM_RECT_BOTTOM( r ) - 1 ); /* Top and left. */ gdk_draw_line( draw, tl, r->left, IM_RECT_BOTTOM( r ) - 1, r->left, r->top ); gdk_draw_line( draw, tl, r->left, r->top, IM_RECT_RIGHT( r ) - 1, r->top ); } /* Paint little ticks ... for marking the edges of the resize handles. */ static void regionview_paint_vtick( GdkDrawable *draw, GdkGC *tl, GdkGC *br, int x, int y, int n ) { gdk_draw_line( draw, br, x, y - 1, x, y - n ); gdk_draw_line( draw, tl, x + 1, y - 1, x + 1, y - n ); } static void regionview_paint_htick( GdkDrawable *draw, GdkGC *tl, GdkGC *br, int x, int y, int n ) { gdk_draw_line( draw, br, x - 1, y, x - n, y ); gdk_draw_line( draw, tl, x - 1, y + 1, x - n, y + 1 ); } /* Paint a region border, enclosing the pixels in r. */ static void regionview_paint_border( GdkDrawable *draw, GdkGC *tl, GdkGC *bg, GdkGC *br, Rect *r, gboolean locked ) { int n = regionview_border_width; Rect our_r = *r; im_rect_marginadjust( &our_r, 1 ); regionview_paint_rect_3d( draw, br, tl, &our_r ); im_rect_marginadjust( &our_r, 1 ); regionview_paint_rect_thick( draw, bg, &our_r, n ); im_rect_marginadjust( &our_r, n ); regionview_paint_rect_3d( draw, tl, br, &our_r ); /* Add little tick marks for corner resizing. Don't bother for very * small rects, or for locked rects. */ if( !locked && r->width > 20 ) { /* Top edge. */ regionview_paint_vtick( draw, tl, br, r->left + 10, r->top - 1, n ); regionview_paint_vtick( draw, tl, br, IM_RECT_RIGHT( r ) - 11, r->top - 1, n ); /* Bottom edge. */ regionview_paint_vtick( draw, tl, br, r->left + 10, IM_RECT_BOTTOM( r ) + n + 1, n ); regionview_paint_vtick( draw, tl, br, IM_RECT_RIGHT( r ) - 11, IM_RECT_BOTTOM( r ) + n + 1, n ); } if( !locked && r->height > 20 ) { /* Left edge. */ regionview_paint_htick( draw, tl, br, r->left - 1, r->top + 10, n ); regionview_paint_htick( draw, tl, br, r->left - 1, IM_RECT_BOTTOM( r ) - 12, n ); /* Right edge. */ regionview_paint_htick( draw, tl, br, IM_RECT_RIGHT( r ) + n + 1, r->top + 10, n ); regionview_paint_htick( draw, tl, br, IM_RECT_RIGHT( r ) + n + 1, IM_RECT_BOTTOM( r ) - 12, n ); } } /* Paint a square area, with a beveled edge. */ static void regionview_paint_area( GdkDrawable *draw, GdkGC *tl, GdkGC *bg, GdkGC *br, Rect *r ) { gdk_draw_rectangle( draw, bg, TRUE, r->left, r->top, r->width, r->height ); regionview_paint_rect_3d( draw, tl, br, r ); } /* Paint a region label. */ static void regionview_paint_label( Regionview *regionview, GdkDrawable *draw, Rect *r, int ascent, const char *txt ) { GtkWidget *widget = GTK_WIDGET( regionview->ip->id ); int n = regionview_label_border; PangoLayout *layout; GdkRectangle grect; /* Clip to this area ... don't want to paint outside label. */ grect.x = r->left; grect.y = r->top; grect.width = r->width; grect.height = r->height; gtk_paint_flat_box( widget->style, draw, regionview->paint_state, GTK_SHADOW_OUT, &grect, widget, "buttondefault", grect.x, grect.y, grect.width, grect.height ); /* Paint text over the top. */ layout = gtk_widget_create_pango_layout( widget, txt ); gtk_paint_layout( widget->style, draw, regionview->paint_state, FALSE, &grect, widget, NULL, r->left + n, r->top + n + ascent, layout ); g_object_unref( layout ); } /* Paint a crosshair, centered at x, y. */ static void regionview_paint_crosshair( GdkDrawable *draw, GdkGC *tl, GdkGC *bg, GdkGC *br, int x, int y ) { const int bw = regionview_border_width / 2 + 1; const int l = regionview_crosshair_length + 2; Rect area; area.left = x - bw - 1 - l; area.top = y - bw; area.width = l; area.height = bw * 2; regionview_paint_area( draw, tl, bg, br, &area ); area.left = x + bw + 1; regionview_paint_area( draw, tl, bg, br, &area ); area.left = x - bw; area.top = y - bw - 1 - l; area.width = bw * 2; area.height = l; regionview_paint_area( draw, tl, bg, br, &area ); area.top = y + bw + 1; regionview_paint_area( draw, tl, bg, br, &area ); } /* Paint the dotted line connecting an arrow or a guide. */ static void regionview_paint_arrow( GdkDrawable *draw, GdkGC *fg, int off, Rect *r ) { static gint8 dash_list[] = { 10, 10 }; gdk_gc_set_dashes( fg, off, dash_list, 2 ); gdk_gc_set_line_attributes( fg, 2, GDK_LINE_DOUBLE_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER ); gdk_draw_line( draw, fg, r->left, r->top, IM_RECT_RIGHT( r ), IM_RECT_BOTTOM( r ) ); gdk_gc_set_line_attributes( fg, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER ); } /* Paint the dotted box for a text preview, or rectangle paint preview. */ static void regionview_paint_box( GdkDrawable *draw, GdkGC *fg, int off, Rect *r ) { static gint8 dash_list[] = { 10, 10 }; gdk_gc_set_dashes( fg, off, dash_list, 2 ); gdk_gc_set_line_attributes( fg, 2, GDK_LINE_DOUBLE_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER ); gdk_draw_line( draw, fg, r->left, r->top, IM_RECT_RIGHT( r ), r->top ); gdk_draw_line( draw, fg, IM_RECT_RIGHT( r ), r->top, IM_RECT_RIGHT( r ), IM_RECT_BOTTOM( r ) ); gdk_draw_line( draw, fg, IM_RECT_RIGHT( r ), IM_RECT_BOTTOM( r ), r->left, IM_RECT_BOTTOM( r ) ); gdk_draw_line( draw, fg, r->left, IM_RECT_BOTTOM( r ), r->left, r->top ); gdk_gc_set_line_attributes( fg, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER ); } /* Apply a function to every rect in a crosshair positioned at (x, y). */ static void * regionview_crosshair_foreach( Regionview *regionview, int x, int y, regionview_rect_fn fn, void *data ) { const int n = regionview_border_width + 2; const int l = regionview_crosshair_length + 2; Rect area; void *res; area.left = x - n/2 - 1 - l; area.top = y - n/2; area.width = l; area.height = n; if( (res = fn( regionview, &area, data )) ) return( res ); area.left = x + n/2 + 1; if( (res = fn( regionview, &area, data )) ) return( res ); area.left = x - n/2; area.top = y - n/2 - 1 - l; area.width = n; area.height = l; if( (res = fn( regionview, &area, data )) ) return( res ); area.top = y + n/2 + 1; if( (res = fn( regionview, &area, data )) ) return( res ); return( NULL ); } /* Apply a function to every rect in a region border positioned at border. */ static void * regionview_border_foreach( Regionview *regionview, Rect *border, regionview_rect_fn fn, void *data ) { const int n = regionview_border_width + 2; Rect area; void *res; area.left = border->left - n; area.top = border->top - n; area.width = border->width + 2*n; area.height = n; if( (res = fn( regionview, &area, data )) ) return( res ); area.top = IM_RECT_BOTTOM( border ); if( (res = fn( regionview, &area, data )) ) return( res ); area.left = border->left - n; area.top = border->top; area.width = n; area.height = border->height; if( (res = fn( regionview, &area, data )) ) return( res ); area.left = IM_RECT_RIGHT( border ); if( (res = fn( regionview, &area, data )) ) return( res ); return( NULL ); } /* Repaint ... as a rect_foreach function. */ static void * regionview_queue_draw_area( Regionview *regionview, Rect *area, void *dummy ) { #ifdef DEBUG_PAINT printf( "regionview_queue_draw_area: at %dx%d size %dx%d\n", area->left, area->top, area->width, area->height ); #endif /*DEBUG_PAINT*/ imagedisplay_queue_draw_area( regionview->ip->id, area ); return( NULL ); } /* Queue draws for all the pixels a region might touch. */ static void regionview_queue_draw( Regionview *regionview ) { Imagedisplay *id = regionview->ip->id; Conversion *conv = id->conv; Rect *area = ®ionview->area; Rect dr; int x, y; switch( regionview->last_type ) { case REGIONVIEW_AREA: case REGIONVIEW_REGION: conversion_im_to_disp_rect( conv, area, &dr ); (void) regionview_border_foreach( regionview, &dr, regionview_queue_draw_area, NULL ); break; case REGIONVIEW_MARK: conversion_im_to_disp( conv, area->left, area->top, &x, &y ); (void) regionview_crosshair_foreach( regionview, x, y, regionview_queue_draw_area, NULL ); break; case REGIONVIEW_ARROW: conversion_im_to_disp_rect( conv, area, &dr ); (void) regionview_crosshair_foreach( regionview, dr.left, dr.top, regionview_queue_draw_area, NULL ); (void) regionview_crosshair_foreach( regionview, IM_RECT_RIGHT( &dr ), IM_RECT_BOTTOM( &dr ), regionview_queue_draw_area, NULL ); im_rect_normalise( &dr ); im_rect_marginadjust( &dr, 2 ); regionview_queue_draw_area( regionview, &dr, NULL ); break; case REGIONVIEW_HGUIDE: case REGIONVIEW_VGUIDE: case REGIONVIEW_LINE: conversion_im_to_disp_rect( conv, area, &dr ); im_rect_normalise( &dr ); im_rect_marginadjust( &dr, 2 ); regionview_queue_draw_area( regionview, &dr, NULL ); break; case REGIONVIEW_BOX: conversion_im_to_disp_rect( conv, area, &dr ); im_rect_normalise( &dr ); im_rect_marginadjust( &dr, -2 ); (void) regionview_border_foreach( regionview, &dr, regionview_queue_draw_area, NULL ); break; default: g_assert( FALSE ); } if( regionview->classmodel ) imagedisplay_queue_draw_area( id, ®ionview->label ); } /* Paint a region ... assume the screen has only the background visible (ie. * we've nothing of this region visible). Clip paints against clip rect (in * xev coordinates) ... either the expose area, or the imagedisplay area. */ static void regionview_paint( Regionview *regionview ) { Imagepresent *ip = regionview->ip; Imagedisplay *id = ip->id; Conversion *conv = id->conv; GtkStyle *style = gtk_widget_get_style( GTK_WIDGET( id ) ); GdkDrawable *draw = GTK_WIDGET( id )->window; int state = regionview->last_paint_state; GdkGC *tl, *br, *bg; Rect dr; tl = style->light_gc[state]; br = style->dark_gc[state]; bg = style->bg_gc[state]; conversion_im_to_disp_rect( conv, ®ionview->area, &dr ); switch( regionview->last_type ) { case REGIONVIEW_REGION: regionview_paint_border( draw, tl, bg, br, &dr, FALSE ); break; case REGIONVIEW_AREA: regionview_paint_border( draw, tl, bg, br, &dr, TRUE ); break; case REGIONVIEW_MARK: regionview_paint_crosshair( draw, tl, bg, br, dr.left, dr.top ); break; case REGIONVIEW_ARROW: regionview_paint_arrow( draw, tl, regionview->dash_offset, &dr ); regionview_paint_crosshair( draw, tl, bg, br, dr.left, dr.top ); regionview_paint_crosshair( draw, tl, bg, br, IM_RECT_RIGHT( &dr ), IM_RECT_BOTTOM( &dr ) ); break; case REGIONVIEW_HGUIDE: case REGIONVIEW_VGUIDE: case REGIONVIEW_LINE: regionview_paint_arrow( draw, tl, regionview->dash_offset, &dr ); break; case REGIONVIEW_BOX: im_rect_normalise( &dr ); regionview_paint_box( draw, tl, regionview->dash_offset, &dr ); break; default: g_assert( FALSE ); } if( regionview->classmodel ) regionview_paint_label( regionview, draw, ®ionview->label, regionview->ascent, vips_buf_all( ®ionview->caption ) ); } /* Stop tracking. */ static void regionview_detach( Regionview *regionview ) { if( regionview->grabbed ) { g_assert( regionview->ip->grabbed == regionview ); #ifdef DEBUG_GRAB printf( "regionview_detach: %p\n", regionview ); #endif /*DEBUG_GRAB*/ regionview->state = REGIONVIEW_WAIT; regionview->paint_state = GTK_STATE_PRELIGHT; regionview->grabbed = FALSE; regionview->ip->grabbed = NULL; imagepresent_scroll_stop( regionview->ip ); } } static void regionview_destroy( GtkObject *object ) { Regionview *regionview; Imagedisplay *id; #ifdef DEBUG_MAKE printf( "regionview_destroy: %p\n", object ); #endif /*DEBUG_MAKE*/ g_return_if_fail( object != NULL ); g_return_if_fail( IS_REGIONVIEW( object ) ); regionview = REGIONVIEW( object ); if( !regionview->first ) regionview_queue_draw( regionview ); regionview->first = FALSE; regionview_detach( regionview ); if( (id = regionview->ip->id) ) { FREESID( regionview->expose_sid, id ); FREESID( regionview->destroy_sid, id ); FREESID( regionview->event_sid, id ); FREESID( regionview->changed_sid, id->conv ); FREESID( regionview->conv_destroy_sid, id->conv ); } FREESID( regionview->model_changed_sid, regionview->classmodel ); IM_FREEF( g_source_remove, regionview->dash_crawl ); IM_FREEF( iwindow_cursor_context_destroy, regionview->cntxt ); vips_buf_destroy( ®ionview->caption ); if( regionview->ip ) { if( regionview->ip->regionview == regionview ) regionview->ip->regionview = NULL; regionview->ip->regionviews = g_slist_remove( regionview->ip->regionviews, regionview ); regionview->ip = NULL; } if( regionview->classmodel ) { regionview->classmodel->views = g_slist_remove( regionview->classmodel->views, regionview ); regionview->classmodel = NULL; } GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* Compute the label geometry. */ static void regionview_label_geo( Regionview *regionview ) { int n = regionview_label_border; const char *str = vips_buf_all( ®ionview->caption ); int width, height; PangoLayout *layout; layout = gtk_widget_create_pango_layout( GTK_WIDGET( regionview->ip->id ), str ); pango_layout_get_pixel_size( layout, &width, &height ); g_object_unref( layout ); regionview->label.width = width + 2 * n; regionview->label.height = height + 2 * n; regionview->ascent = 0; } static void regionview_refresh_label( Regionview *regionview ) { if( regionview->classmodel ) { Row *row = HEAPMODEL( regionview->classmodel )->row; vips_buf_rewind( ®ionview->caption ); row_qualified_name_relative( row->ws->sym, row, ®ionview->caption ); regionview_label_geo( regionview ); } } /* Move label to try to keep it within the window, and away from the * selected pixels. */ static void regionview_position_label( Regionview *regionview ) { Imagepresent *ip = regionview->ip; Conversion *conv = ip->id->conv; Rect *visible = &conv->visible; Rect *label = ®ionview->label; const int b = regionview_border_width + 2; Rect dr; if( regionview->label_geo ) { regionview_refresh_label( regionview ); regionview->label_geo = FALSE; } conversion_im_to_disp_rect( conv, ®ionview->area, &dr ); switch( regionview->type ) { case REGIONVIEW_REGION: case REGIONVIEW_AREA: case REGIONVIEW_BOX: if( dr.top > visible->top + label->height + b ) { /* Space above region for label. */ label->left = dr.left - b; label->top = dr.top - label->height - b; } else if( dr.left > visible->left + label->width + b ) { /* Space to left of region for label */ label->left = dr.left - label->width - b; label->top = dr.top - b; } else if( IM_RECT_RIGHT( &dr ) < IM_RECT_RIGHT( visible ) - label->width - b ) { /* Space at right. */ label->left = IM_RECT_RIGHT( &dr ) + b; label->top = dr.top - b; } else if( IM_RECT_BOTTOM( &dr ) < IM_RECT_BOTTOM( visible ) - label->height - b ) { /* Space at bottom. */ label->left = dr.left - b; label->top = IM_RECT_BOTTOM( &dr ) + b; } else { /* Inside top left. */ label->left = dr.left; label->top = dr.top; } break; case REGIONVIEW_HGUIDE: case REGIONVIEW_VGUIDE: case REGIONVIEW_MARK: case REGIONVIEW_ARROW: case REGIONVIEW_LINE: /* Space above? */ if( dr.top > visible->top + label->height + b/2 + 2 ) { if( dr.left > IM_RECT_RIGHT( visible ) - label->width - b/2 - 2 ) { /* Above left. */ label->left = dr.left - b/2 - 2 - label->width; label->top = dr.top - b/2 - 2 - label->height; } else { /* Above right. */ label->left = dr.left + b/2 + 2; label->top = dr.top - b/2 - 2 - label->height; } } else if( dr.left > IM_RECT_RIGHT( visible ) - label->width - b/2 - 2 ) { /* Below left. */ label->left = dr.left - b/2 - 2 - label->width; label->top = dr.top + b/2 + 2; } else { /* Below right. */ label->left = dr.left + b/2 + 2; label->top = dr.top + b/2 + 2; } break; default: g_assert( FALSE ); } } static Rect * regionview_get_model( Regionview *regionview ) { Classmodel *classmodel = regionview->classmodel; iRegionInstance *instance; Rect *model_area; /* If we have a class, update from the inside of that. */ if( classmodel && (instance = classmodel_get_instance( classmodel )) ) model_area = &instance->area; else model_area = regionview->model_area; return( model_area ); } /* Update our_area from the model. Translate to our cods too: we always have * x/y in 0 to xsize/ysize. */ static void regionview_update_from_model( Regionview *regionview ) { Rect *model_area = regionview_get_model( regionview ); #ifdef DEBUG printf( "regionview_update_from_model: model is %dx%d size %dx%d\n", model_area->left, model_area->top, model_area->width, model_area->height ); #endif /*DEBUG*/ regionview->our_area = *model_area; #ifdef DEBUG printf( "regionview_update_from_model: set regionview to %dx%d size %dx%d\n", regionview->our_area.left, regionview->our_area.top, regionview->our_area.width, regionview->our_area.height ); #endif /*DEBUG*/ } /* Update the model from our_area. */ static void regionview_model_update( Regionview *regionview ) { Classmodel *classmodel = regionview->classmodel; Rect *model_area = regionview_get_model( regionview ); #ifdef DEBUG printf( "regionview_model_update: regionview is %dx%d size %dx%d\n", regionview->our_area.left, regionview->our_area.top, regionview->our_area.width, regionview->our_area.height ); #endif /*DEBUG*/ *model_area = regionview->our_area; if( classmodel ) { classmodel_update( classmodel ); if( CALC_RECOMP_REGION ) symbol_recalculate_all(); } /* Refresh immediately .. gives faster feedback during drag. */ vobject_refresh( VOBJECT( regionview ) ); #ifdef DEBUG printf( "regionview_model_update: set model to %dx%d size %dx%d\n", model_area->left, model_area->top, model_area->width, model_area->height ); #endif /*DEBUG*/ } /* Our model has changed ... undraw in the old position, draw in the new * position. */ static void regionview_refresh( vObject *vobject ) { Regionview *regionview = REGIONVIEW( vobject ); #ifdef DEBUG printf( "regionview_refresh1: %dx%d size %dx%d\n", regionview->our_area.left, regionview->our_area.top, regionview->our_area.width, regionview->our_area.height ); #endif /*DEBUG*/ /* Update our_area from model. */ regionview_update_from_model( regionview ); #ifdef DEBUG printf( "regionview_refresh2: %dx%d size %dx%d\n", regionview->our_area.left, regionview->our_area.top, regionview->our_area.width, regionview->our_area.height ); #endif /*DEBUG*/ if( !regionview->first ) regionview_queue_draw( regionview ); regionview->first = FALSE; /* Set new position. */ regionview->area = regionview->our_area; regionview->last_paint_state = regionview->paint_state; regionview->last_type = regionview->type; /* Choose a new label position. */ regionview_position_label( regionview ); /* Draw in the new place, clip against imagedisplay draw area. */ regionview_queue_draw( regionview ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void regionview_edit_cb( GtkWidget *menu, Regionview *regionview, Imagepresent *ip ) { model_edit( GTK_WIDGET( ip ), MODEL( regionview->classmodel ) ); } static void regionview_clone_cb( GtkWidget *menu, Regionview *regionview, Imagepresent *ip ) { Row *row = HEAPMODEL( regionview->classmodel )->row; Workspace *ws = row->top_col->ws; if( row->top_row != row ) { error_top( _( "Can't duplicate." ) ); error_sub( "%s", _( "You can only duplicate top level regions." ) ); iwindow_alert( GTK_WIDGET( regionview->ip ), GTK_MESSAGE_INFO ); return; } workspace_deselect_all( ws ); row_select( row ); if( !workspace_selected_duplicate( ws ) ) iwindow_alert( GTK_WIDGET( regionview ), GTK_MESSAGE_ERROR ); workspace_deselect_all( ws ); symbol_recalculate_all(); } static void regionview_clear_edited_cb( GtkWidget *menu, Regionview *regionview, Imagepresent *ip ) { (void) icontainer_map_all( ICONTAINER( regionview->classmodel ), (icontainer_map_fn) model_clear_edited, NULL ); symbol_recalculate_all(); } static void regionview_remove_yes( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Regionview *regionview = REGIONVIEW( client ); Row *row = HEAPMODEL( regionview->classmodel )->row; IDESTROY( row->sym ); nfn( sys, IWINDOW_YES ); } static void regionview_remove_cb( GtkWidget *menu, Regionview *regionview, Imagepresent *ip ) { Row *row = HEAPMODEL( regionview->classmodel )->row; if( row->top_row != row ) { error_top( _( "Can't delete." ) ); error_sub( _( "You can only delete top level regions." ) ); iwindow_alert( GTK_WIDGET( regionview->ip ), GTK_MESSAGE_INFO ); return; } box_yesno( GTK_WIDGET( ip ), regionview_remove_yes, iwindow_true_cb, regionview, iwindow_notify_null, NULL, GTK_STOCK_DELETE, _( "Delete Region?" ), _( "Are you sure you want to delete Region \"%s\"?" ), vips_buf_all( ®ionview->caption ) ); } static void regionview_class_init( RegionviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; GtkWidget *pane; parent_class = g_type_class_peek_parent( class ); object_class->destroy = regionview_destroy; /* Create signals. */ /* Init methods. */ vobject_class->refresh = regionview_refresh; /* Other init. */ pane = regionview_popup_menu = popup_build( _( "Region menu" ) ); popup_add_but( pane, _( "_Edit" ), POPUP_FUNC( regionview_edit_cb ) ); popup_add_but( pane, STOCK_DUPLICATE, POPUP_FUNC( regionview_clone_cb ) ); popup_add_but( pane, _( "_Reset" ), POPUP_FUNC( regionview_clear_edited_cb ) ); menu_add_sep( pane ); popup_add_but( pane, GTK_STOCK_DELETE, POPUP_FUNC( regionview_remove_cb ) ); } static void regionview_init( Regionview *regionview ) { static Rect empty_rect = { -1, -1, -1, -1 }; #ifdef DEBUG_MAKE printf( "regionview_init\n" ); #endif /*DEBUG_MAKE*/ regionview->type = REGIONVIEW_MARK; regionview->frozen = TRUE; regionview->state = REGIONVIEW_WAIT; regionview->resize = REGIONVIEW_RESIZE_NONE; regionview->dx = -1; regionview->dy = -1; regionview->grabbed = FALSE; regionview->classmodel = NULL; regionview->ip = NULL; regionview->cntxt = NULL; regionview->expose_sid = 0; regionview->destroy_sid = 0; regionview->event_sid = 0; regionview->changed_sid = 0; regionview->conv_destroy_sid = 0; regionview->model_area = NULL; regionview->paint_state = GTK_STATE_NORMAL; regionview->area = empty_rect; regionview->label = empty_rect; regionview->ascent = 0; regionview->dash_offset = 0; regionview->dash_crawl = 0; regionview->last_paint_state = (GtkStateType) -1; regionview->last_type = (RegionviewType) -1; regionview->first = TRUE; regionview->label_geo = TRUE; vips_buf_init_dynamic( ®ionview->caption, REGIONVIEW_LABEL_MAX ); gtk_widget_set_name( GTK_WIDGET( regionview ), "regionview_widget" ); } GtkType regionview_get_type( void ) { static GtkType regionview_type = 0; if( !regionview_type ) { static const GtkTypeInfo info = { "Regionview", sizeof( Regionview ), sizeof( RegionviewClass ), (GtkClassInitFunc) regionview_class_init, (GtkObjectInitFunc) regionview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; regionview_type = gtk_type_unique( TYPE_VIEW, &info ); } return( regionview_type ); } /* Test for rect touches rect (non-empty intersection). */ static void * regionview_rect_touching( Regionview *regionview, Rect *a, Rect *b ) { Rect overlap; im_rect_intersectrect( a, b, &overlap ); if( !im_rect_isempty( &overlap ) ) return( regionview ); else return( NULL ); } /* Does expose rect touch the mark positioned at mark_x/mark_y. Include a big * grab handle in the centre of the crosshair. */ static gboolean regionview_rect_touches_mark( Regionview *regionview, int mark_x, int mark_y, Rect *expose ) { Conversion *conv = regionview->ip->id->conv; Rect tiny; int x, y; conversion_im_to_disp( conv, mark_x, mark_y, &x, &y ); if( regionview_crosshair_foreach( regionview, x, y, (regionview_rect_fn) regionview_rect_touching, expose ) ) return( TRUE ); /* ... and the centre of the crosshairs. */ tiny.left = x; tiny.top = y; tiny.width = 1; tiny.height = 1; im_rect_marginadjust( &tiny, regionview_crosshair_centre ); if( regionview_rect_touching( regionview, &tiny, expose ) ) return( TRUE ); return( FALSE ); } /* Test for rect intersects some part of region. */ static gboolean regionview_rect_touches_region( Regionview *regionview, Rect *expose ) { Conversion *conv = regionview->ip->id->conv; Rect canvas_area; if( regionview->classmodel && regionview_rect_touching( regionview, ®ionview->label, expose ) ) return( TRUE ); switch( regionview->type ) { case REGIONVIEW_REGION: case REGIONVIEW_AREA: case REGIONVIEW_BOX: case REGIONVIEW_LINE: conversion_im_to_disp_rect( conv, ®ionview->area, &canvas_area ); im_rect_normalise( &canvas_area ); if( regionview_border_foreach( regionview, &canvas_area, (regionview_rect_fn) regionview_rect_touching, expose ) ) return( TRUE ); break; case REGIONVIEW_MARK: if( regionview_rect_touches_mark( regionview, regionview->area.left, regionview->area.top, expose ) ) return( TRUE ); break; case REGIONVIEW_ARROW: /* Test two marks first. */ if( regionview_rect_touches_mark( regionview, regionview->area.left, regionview->area.top, expose ) ) return( TRUE ); if( regionview_rect_touches_mark( regionview, IM_RECT_RIGHT( ®ionview->area ), IM_RECT_BOTTOM( ®ionview->area ), expose ) ) return( TRUE ); /* Spot in main area too ... for the dotted line. Also avoid * zero-width/height areas for h and v lines. */ conversion_im_to_disp_rect( conv, ®ionview->area, &canvas_area ); im_rect_normalise( &canvas_area ); im_rect_marginadjust( &canvas_area, 1 ); if( regionview_rect_touching( regionview, &canvas_area, expose ) ) return( TRUE ); break; case REGIONVIEW_HGUIDE: case REGIONVIEW_VGUIDE: conversion_im_to_disp_rect( conv, ®ionview->area, &canvas_area ); im_rect_marginadjust( &canvas_area, 5 ); if( regionview_rect_touching( regionview, &canvas_area, expose ) ) return( TRUE ); break; default: g_assert( FALSE ); } return( FALSE ); } /* From the expose event. */ static void regionview_expose( Regionview *regionview, Rect *expose ) { #ifdef DEBUG_PAINT printf( "regionview_expose: at %dx%d size %dx%d\n", expose->left, expose->top, expose->width, expose->height ); #endif /*DEBUG_PAINT*/ g_assert( expose->width >= 0 && expose->height >= 0 ); /* If we've not finished init, don't paint. */ if( regionview->first ) return; /* If the expose doesn't touch the region, don't bother painting. */ if( !regionview_rect_touches_region( regionview, expose ) ) return; regionview_paint( regionview ); } static void regionview_model_changed_cb( Classmodel *classmodel, Regionview *regionview ) { vobject_refresh_queue( VOBJECT( regionview ) ); } static gboolean regionview_expose_cb( Imagedisplay *id, GdkEventExpose *event, Regionview *regionview ) { Rect expose; expose.left = event->area.x; expose.top = event->area.y; expose.width = event->area.width; expose.height = event->area.height; regionview_expose( regionview, &expose ); return( FALSE ); } /* Test for point is in the grab area of a region border or label. */ static gboolean regionview_point_in_region( Regionview *regionview, int x, int y ) { Rect r; r.left = x; r.top = y; r.width = 1; r.height = 1; return( regionview_rect_touches_region( regionview, &r ) ); } /* Given a position, find the sort of resize we should allow. */ static RegionviewResize regionview_find_resize( Regionview *regionview, int x, int y ) { Conversion *conv = regionview->ip->id->conv; Rect canvas_area, tiny; int dx, dy; if( im_rect_includespoint( ®ionview->label, x, y ) ) return( REGIONVIEW_RESIZE_EDIT ); conversion_im_to_disp_rect( conv, ®ionview->area, &canvas_area ); dx = x - canvas_area.left; dy = y - canvas_area.top; switch( regionview->type ) { case REGIONVIEW_REGION: if( dx > canvas_area.width - 10 ) { if( dy > canvas_area.height - 10 ) return( REGIONVIEW_RESIZE_BOTTOMRIGHT ); else if( dy < 10 ) return( REGIONVIEW_RESIZE_TOPRIGHT ); else return( REGIONVIEW_RESIZE_RIGHT ); } else if( dx < 10 ) { if( dy > canvas_area.height - 10 ) return( REGIONVIEW_RESIZE_BOTTOMLEFT ); else if( dy < 10 ) return( REGIONVIEW_RESIZE_TOPLEFT ); else return( REGIONVIEW_RESIZE_LEFT ); } else { if( dy < canvas_area.height / 2 ) return( REGIONVIEW_RESIZE_TOP ); else return( REGIONVIEW_RESIZE_BOTTOM ); } break; case REGIONVIEW_MARK: case REGIONVIEW_AREA: return( REGIONVIEW_RESIZE_MOVE ); case REGIONVIEW_ARROW: tiny.left = x; tiny.top = y; tiny.width = 1; tiny.height = 1; if( regionview_crosshair_foreach( regionview, canvas_area.left, canvas_area.top, (regionview_rect_fn) regionview_rect_touching, &tiny ) ) return( REGIONVIEW_RESIZE_TOPLEFT ); if( regionview_crosshair_foreach( regionview, IM_RECT_RIGHT( &canvas_area ), IM_RECT_BOTTOM( &canvas_area ), (regionview_rect_fn) regionview_rect_touching, &tiny ) ) return( REGIONVIEW_RESIZE_BOTTOMRIGHT ); /* Extra tests ... allow grabs in the centre of the crosshairs * too. */ tiny.left = IM_RECT_RIGHT( &canvas_area ); tiny.top = IM_RECT_BOTTOM( &canvas_area ); tiny.width = 1; tiny.height = 1; im_rect_marginadjust( &tiny, regionview_crosshair_centre ); if( im_rect_includespoint( &tiny, x, y ) ) return( REGIONVIEW_RESIZE_BOTTOMRIGHT ); tiny.left = canvas_area.left; tiny.top = canvas_area.top; tiny.width = 1; tiny.height = 1; im_rect_marginadjust( &tiny, regionview_crosshair_centre ); if( im_rect_includespoint( &tiny, x, y ) ) return( REGIONVIEW_RESIZE_TOPLEFT ); break; case REGIONVIEW_VGUIDE: case REGIONVIEW_HGUIDE: im_rect_marginadjust( &canvas_area, 5 ); if( im_rect_includespoint( &canvas_area, x, y ) ) return( REGIONVIEW_RESIZE_MOVE ); break; case REGIONVIEW_BOX: case REGIONVIEW_LINE: break; default: g_assert( FALSE ); } return( REGIONVIEW_RESIZE_NONE ); } /* Right button press event. */ static gint regionview_right_press( Regionview *regionview, GdkEvent *ev, int x, int y ) { if( im_rect_includespoint( ®ionview->label, x, y ) ) { popup_show( GTK_WIDGET( regionview ), ev ); return( TRUE ); } return( FALSE ); } /* Get ready to track this region. See imagepresent.c. */ void regionview_attach( Regionview *regionview, int x, int y ) { Imagepresent *ip = regionview->ip; Conversion *conv = ip->id->conv; int dx, dy; g_assert( !regionview->grabbed ); g_assert( !regionview->ip->grabbed ); #ifdef DEBUG_GRAB printf( "regionview_attach: %p\n", regionview ); #endif /*DEBUG_GRAB*/ switch( regionview->resize ) { case REGIONVIEW_RESIZE_NONE: regionview->resize = REGIONVIEW_RESIZE_BOTTOMRIGHT; regionview->state = REGIONVIEW_RESIZE; break; case REGIONVIEW_RESIZE_MOVE: case REGIONVIEW_RESIZE_EDIT: regionview->state = REGIONVIEW_MOVE; break; default: regionview->state = REGIONVIEW_RESIZE; break; } regionview->paint_state = GTK_STATE_ACTIVE; iwindow_cursor_context_set_cursor( regionview->cntxt, regionview_cursors[regionview->resize] ); regionview->grabbed = TRUE; regionview->ip->grabbed = regionview; conversion_im_to_disp( conv, regionview->our_area.left, regionview->our_area.top, &dx, &dy ); regionview->dx = dx - x; regionview->dy = dy - y; } /* Left button press event. */ static gint regionview_left_press( Regionview *regionview, GdkEvent *ev, int x, int y ) { gboolean handled = FALSE; if( !regionview_point_in_region( regionview, x, y ) ) return( FALSE ); switch( regionview->state ) { case REGIONVIEW_WAIT: regionview->resize = regionview_find_resize( regionview, x, y ); if( regionview->resize != REGIONVIEW_RESIZE_NONE ) { regionview_attach( regionview, x, y ); handled = TRUE; } break; case REGIONVIEW_MOVE: case REGIONVIEW_RESIZE: break; default: g_assert( FALSE ); } return( handled ); } /* Left button release event. */ static gint regionview_left_release( Regionview *regionview, GdkEvent *ev ) { switch( regionview->state ) { case REGIONVIEW_WAIT: break; case REGIONVIEW_MOVE: case REGIONVIEW_RESIZE: regionview_detach( regionview ); if( !CALC_RECOMP_REGION ) symbol_recalculate_all(); break; } return( FALSE ); } static void regionview_resize_area( Regionview *regionview, int ix, int iy ) { Imagepresent *ip = regionview->ip; Conversion *conv = ip->id->conv; IMAGE *im = imageinfo_get( FALSE, conv->ii ); Rect *our_area = ®ionview->our_area; int th = regionview_morph_threshold / conversion_dmag( conv->mag ); int bot = our_area->top + our_area->height; int ri = our_area->left + our_area->width; int rx = ix - our_area->left; int ry = iy - our_area->top; /* If we're not frozen, do an unconstrained resize. */ if( !regionview->frozen ) { switch( regionview->resize ) { case REGIONVIEW_RESIZE_RIGHT: our_area->width = IM_CLIP( -our_area->left, rx, im->Xsize - our_area->left ); break; case REGIONVIEW_RESIZE_BOTTOM: our_area->height = IM_CLIP( -our_area->top, ry, im->Ysize - our_area->top ); break; case REGIONVIEW_RESIZE_MOVE: /* Get this for POINT on create ... treat as * BOTTOMRIGHT. */ case REGIONVIEW_RESIZE_BOTTOMRIGHT: our_area->width = IM_CLIP( -our_area->left, rx, im->Xsize - our_area->left ); our_area->height = IM_CLIP( -our_area->top, ry, im->Ysize - our_area->top ); break; case REGIONVIEW_RESIZE_LEFT: our_area->left = IM_CLIP( 0, ix, im->Xsize - 1 ); our_area->width = ri - our_area->left; break; case REGIONVIEW_RESIZE_TOP: our_area->top = IM_CLIP( 0, iy, im->Ysize - 1 ); our_area->height = bot - our_area->top; break; case REGIONVIEW_RESIZE_TOPLEFT: our_area->top = IM_CLIP( 0, iy, im->Ysize - 1 ); our_area->left = IM_CLIP( 0, ix, im->Xsize - 1 ); our_area->width = ri - our_area->left; our_area->height = bot - our_area->top; break; case REGIONVIEW_RESIZE_TOPRIGHT: our_area->top = IM_CLIP( 0, iy, im->Ysize - 1 ); our_area->height = bot - our_area->top; our_area->width = IM_CLIP( -our_area->left, rx, im->Xsize - our_area->left ); break; case REGIONVIEW_RESIZE_BOTTOMLEFT: our_area->left = IM_CLIP( 0, ix, im->Xsize - 1 ); our_area->width = ri - our_area->left; our_area->height = IM_CLIP( -our_area->top, ry, im->Ysize - our_area->top ); break; default: g_assert( FALSE ); } if( abs( our_area->width ) < th && abs( our_area->height - im->Ysize ) < th ) regionview->type = REGIONVIEW_VGUIDE; else if( abs( our_area->height ) < th && abs( our_area->width - im->Xsize ) < th ) regionview->type = REGIONVIEW_HGUIDE; else if( abs( our_area->width ) < th && abs( our_area->height ) < th ) regionview->type = REGIONVIEW_MARK; else if( our_area->width > 0 && our_area->height > 0 ) regionview->type = REGIONVIEW_REGION; else regionview->type = REGIONVIEW_ARROW; } else { /* We're frozen ... resize should be tightly constrained. */ switch( regionview->type ) { case REGIONVIEW_REGION: switch( regionview->resize ) { case REGIONVIEW_RESIZE_RIGHT: our_area->width = IM_CLIP( 1, rx, im->Xsize - our_area->left ); break; case REGIONVIEW_RESIZE_BOTTOM: our_area->height = IM_CLIP( 1, ry, im->Ysize - our_area->top ); break; case REGIONVIEW_RESIZE_BOTTOMRIGHT: our_area->width = IM_CLIP( 1, rx, im->Xsize - our_area->left ); our_area->height = IM_CLIP( 1, ry, im->Ysize - our_area->top ); break; case REGIONVIEW_RESIZE_TOP: our_area->top = IM_CLIP( 0, iy, bot - 1 ); our_area->height = bot - our_area->top; break; case REGIONVIEW_RESIZE_LEFT: our_area->left = IM_CLIP( 0, ix, ri - 1 ); our_area->width = ri - our_area->left; break; case REGIONVIEW_RESIZE_TOPLEFT: our_area->left = IM_CLIP( 0, ix, ri - 1 ); our_area->width = ri - our_area->left; our_area->top = IM_CLIP( 0, iy, bot - 1 ); our_area->height = bot - our_area->top; break; case REGIONVIEW_RESIZE_TOPRIGHT: our_area->top = IM_CLIP( 0, iy, bot - 1 ); our_area->height = bot - our_area->top; our_area->width = IM_CLIP( 1, rx, im->Xsize - our_area->left ); break; case REGIONVIEW_RESIZE_BOTTOMLEFT: our_area->left = IM_CLIP( 0, ix, ri - 1 ); our_area->width = ri - our_area->left; our_area->height = IM_CLIP( 1, ry, im->Ysize - our_area->top ); break; default: g_assert( FALSE ); } break; case REGIONVIEW_ARROW: case REGIONVIEW_LINE: case REGIONVIEW_BOX: switch( regionview->resize ) { case REGIONVIEW_RESIZE_TOPLEFT: our_area->left = IM_CLIP( 0, ix, im->Xsize ); our_area->width = ri - our_area->left; our_area->top = IM_CLIP( 0, iy, im->Ysize ); our_area->height = bot - our_area->top; break; case REGIONVIEW_RESIZE_BOTTOMRIGHT: our_area->width = IM_CLIP( -our_area->left, rx, im->Xsize - our_area->left ); our_area->height = IM_CLIP( -our_area->top, ry, im->Ysize - our_area->top ); break; default: g_assert( FALSE ); } break; case REGIONVIEW_MARK: our_area->left = IM_CLIP( 0, ix, im->Xsize - 1 ); our_area->top = IM_CLIP( 0, iy, im->Ysize - 1 ); our_area->width = 0; our_area->height = 0; break; case REGIONVIEW_HGUIDE: our_area->top = IM_CLIP( 0, iy, im->Ysize - 1 ); break; case REGIONVIEW_VGUIDE: our_area->left = IM_CLIP( 0, ix, im->Xsize - 1 ); break; default: g_assert( FALSE ); } } } /* Change the state. */ static void regionview_set_paint_state( Regionview *regionview, GtkStateType paint_state ) { if( regionview->paint_state != paint_state ) { regionview->paint_state = paint_state; vobject_refresh_queue( VOBJECT( regionview ) ); } } /* A motion event while we're grabbed. */ static void regionview_motion_grab( Regionview *regionview, int x, int y ) { Imagepresent *ip = regionview->ip; Imagemodel *imagemodel = ip->imagemodel; Conversion *conv = imagemodel->conv; Rect *visible = &imagemodel->visible; Rect *our_area = ®ionview->our_area; Rect snap; IMAGE *im; int ix, iy; #ifdef DEBUG printf( "regionview_motion_grab:\n" ); printf( "cods: %dx%d size %dx%d\n", our_area->left, our_area->top, our_area->width, our_area->height ); #endif /*DEBUG*/ switch( regionview->state ) { case REGIONVIEW_MOVE: conversion_disp_to_im( conv, x + regionview->dx, y + regionview->dy, &ix, &iy ); im = imageinfo_get( FALSE, conv->ii ); switch( regionview->type ) { case REGIONVIEW_REGION: case REGIONVIEW_AREA: our_area->left = IM_CLIP( 0, ix, im->Xsize - our_area->width ); our_area->top = IM_CLIP( 0, iy, im->Ysize - our_area->height ); break; case REGIONVIEW_ARROW: our_area->left = IM_CLIP( IM_MAX( 0, -our_area->width ), ix, IM_MIN( im->Xsize - 1, im->Xsize - our_area->width ) ); our_area->top = IM_CLIP( IM_MAX( 0, -our_area->height ), iy, IM_MIN( im->Ysize - 1, im->Ysize - our_area->height ) ); break; case REGIONVIEW_MARK: case REGIONVIEW_HGUIDE: case REGIONVIEW_VGUIDE: our_area->left = IM_CLIP( 0, ix, im->Xsize - our_area->width - 1 ); our_area->top = IM_CLIP( 0, iy, im->Ysize - our_area->height - 1 ); break; case REGIONVIEW_LINE: case REGIONVIEW_BOX: our_area->left = ix; our_area->top = iy; break; default: g_assert( FALSE ); } snap = *our_area; conversion_im_to_disp_rect( conv, &snap, &snap ); if( imagepresent_snap_rect( ip, &snap, &snap ) ) { conversion_disp_to_im_rect( conv, &snap, &snap ); our_area->left = snap.left; our_area->top = snap.top; } regionview_model_update( regionview ); break; case REGIONVIEW_RESIZE: imagepresent_snap_point( ip, x, y, &x, &y ); conversion_disp_to_im( conv, x, y, &ix, &iy ); regionview_resize_area( regionview, ix, iy ); regionview_model_update( regionview ); break; default: break; } if( !im_rect_includespoint( visible, x, y ) ) { int u, v; if( x < visible->left ) u = -8; else if( x > IM_RECT_RIGHT( visible ) ) u = 8; else u = 0; if( y < visible->top ) v = -8; else if( y > IM_RECT_BOTTOM( visible ) ) v = 8; else v = 0; imagepresent_scroll_start( regionview->ip, u, v ); } else imagepresent_scroll_stop( regionview->ip ); } #ifdef EVENT static char * resize_to_str( RegionviewResize resize ) { switch( resize ) { case REGIONVIEW_RESIZE_NONE: return( "REGIONVIEW_RESIZE_NONE" ); case REGIONVIEW_RESIZE_MOVE: return( "REGIONVIEW_RESIZE_MOVE" ); case REGIONVIEW_RESIZE_EDIT: return( "REGIONVIEW_RESIZE_EDIT" ); case REGIONVIEW_RESIZE_TOPLEFT: return( "REGIONVIEW_RESIZE_TOPLEFT" ); case REGIONVIEW_RESIZE_TOP: return( "REGIONVIEW_RESIZE_TOP" ); case REGIONVIEW_RESIZE_TOPRIGHT: return( "REGIONVIEW_RESIZE_TOPRIGHT" ); case REGIONVIEW_RESIZE_RIGHT: return( "REGIONVIEW_RESIZE_RIGHT" ); case REGIONVIEW_RESIZE_BOTTOMRIGHT: return( "REGIONVIEW_RESIZE_BOTTOMRIGHT" ); case REGIONVIEW_RESIZE_BOTTOM: return( "REGIONVIEW_RESIZE_BOTTOM" ); case REGIONVIEW_RESIZE_BOTTOMLEFT: return( "REGIONVIEW_RESIZE_BOTTOMLEFT" ); case REGIONVIEW_RESIZE_LEFT: return( "REGIONVIEW_RESIZE_LEFT" ); case REGIONVIEW_RESIZE_LAST: return( "REGIONVIEW_RESIZE_LAST" ); default: g_assert( 0 ); } } #endif /*EVENT*/ /* Motion event. */ static gint regionview_motion( Regionview *regionview, GdkEvent *ev, int x, int y ) { GdkWindow *win = GTK_WIDGET( regionview->ip->id )->window; RegionviewResize resize; #ifdef EVENT printf( "regionview_motion: %p, %d x %d\n", regionview, x, y ); #endif /*EVENT*/ /* We've got hints turned on, so we have to read the pointer. */ gdk_window_get_pointer( win, &x, &y, NULL ); switch( regionview->state ) { case REGIONVIEW_WAIT: if( regionview_point_in_region( regionview, x, y ) ) { resize = regionview_find_resize( regionview, x, y ); iwindow_cursor_context_set_cursor( regionview->cntxt, regionview_cursors[resize] ); regionview_set_paint_state( regionview, GTK_STATE_PRELIGHT ); } else { iwindow_cursor_context_set_cursor( regionview->cntxt, IWINDOW_SHAPE_NONE ); regionview_set_paint_state( regionview, GTK_STATE_NORMAL ); } break; case REGIONVIEW_MOVE: case REGIONVIEW_RESIZE: if( regionview->grabbed ) regionview_motion_grab( regionview, x, y ); break; default: g_assert( FALSE ); } return( FALSE ); } /* Main event loop. */ static gint regionview_event_cb( GtkWidget *widget, GdkEvent *ev, Regionview *regionview ) { Imagepresent *ip = regionview->ip; Imagemodel *imagemodel = ip->imagemodel; gboolean handled = FALSE; #ifdef EVENT if( ev->type == GDK_BUTTON_PRESS ) printf( "regionview_event: GDK_BUTTON_PRESS\n" ); if( ev->type == GDK_MOTION_NOTIFY ) printf( "regionview_event: GDK_MOTION_NOTIFY\n" ); #endif /*EVENT*/ /* Only manipulate regions if we're in SELECT mode ... don't want to * drag regions while we're panning, for example. Exception ... we can * drag/resize floating regions any time. */ if( imagemodel->state != IMAGEMODEL_SELECT && regionview->classmodel ) return( FALSE ); /* If there's a regionview grabbed, only that regionview responds to * events. */ if( regionview->ip->grabbed && regionview->ip->grabbed != regionview ) return( FALSE ); switch( ev->type ) { case GDK_BUTTON_PRESS: switch( ev->button.button ) { case 1: handled = regionview_left_press( regionview, ev, ev->button.x, ev->button.y ); break; case 3: handled = regionview_right_press( regionview, ev, ev->button.x, ev->button.y ); break; default: break; } break; case GDK_2BUTTON_PRESS: switch( ev->button.button ) { case 1: if( regionview->state == REGIONVIEW_MOVE && regionview->resize == REGIONVIEW_RESIZE_EDIT && regionview->classmodel ) { model_edit( GTK_WIDGET( ip ), MODEL( regionview->classmodel ) ); handled = TRUE; } break; default: break; } break; case GDK_BUTTON_RELEASE: switch( ev->button.button ) { case 1: handled = regionview_left_release( regionview, ev ); break; default: break; } break; case GDK_MOTION_NOTIFY: handled = regionview_motion( regionview, ev, ev->button.x, ev->button.y ); break; default: break; } return( handled ); } /* The conversion on our image has changed ... eg. on zoom in/out we need to * rethink the label position. */ static void regionview_changed_cb( Model *model, Regionview *regionview ) { #ifdef DEBUG printf( "regionview_changed\n" ); #endif /*DEBUG*/ vobject_refresh_queue( VOBJECT( regionview ) ); } /* The conversion on our image has been destroyed ... make sure we won't try * to disconnect when we go too. */ static void regionview_conv_destroy_cb( Model *model, Regionview *regionview ) { regionview->changed_sid = 0; regionview->conv_destroy_sid = 0; } static gboolean regionview_dash_crawl_cb( Regionview *regionview ) { /* Don't for regions, areas and points ... no lines in 'em. */ if( regionview->type != REGIONVIEW_REGION && regionview->type != REGIONVIEW_MARK && regionview->type != REGIONVIEW_AREA ) { regionview->dash_offset += 3; /* Don't repaint before the first expose. last_type etc. * won't have been inited properly yet. */ if( !regionview->first ) regionview_queue_draw( regionview ); } return( TRUE ); } static void regionview_setup( Regionview *regionview, Classmodel *classmodel, Rect *model_area, Imagepresent *ip ) { iWindow *iwnd; regionview->classmodel = classmodel; regionview->ip = ip; regionview->model_area = model_area; regionview->our_area = *model_area; regionview->model_changed_sid = 0; ip->regionviews = g_slist_prepend( ip->regionviews, regionview ); if( classmodel ) { classmodel->views = g_slist_prepend( classmodel->views, regionview ); regionview->model_changed_sid = g_signal_connect( G_OBJECT( classmodel ), "changed", G_CALLBACK( regionview_model_changed_cb ), regionview ); } regionview->expose_sid = g_signal_connect_after( GTK_OBJECT( ip->id ), "expose_event", GTK_SIGNAL_FUNC( regionview_expose_cb ), regionview ); regionview->destroy_sid = gtk_signal_connect_object( GTK_OBJECT( ip->id ), "destroy", GTK_SIGNAL_FUNC( gtk_widget_destroy ), GTK_OBJECT( regionview ) ); regionview->event_sid = gtk_signal_connect( GTK_OBJECT( ip->id ), "event", GTK_SIGNAL_FUNC( regionview_event_cb ), regionview ); regionview->changed_sid = g_signal_connect( G_OBJECT( ip->id->conv ), "changed", G_CALLBACK( regionview_changed_cb ), regionview ); regionview->conv_destroy_sid = g_signal_connect( G_OBJECT( ip->id->conv ), "destroy", G_CALLBACK( regionview_conv_destroy_cb ), regionview ); iwnd = IWINDOW( gtk_widget_get_toplevel( GTK_WIDGET( ip ) ) ); regionview->cntxt = iwindow_cursor_context_new( iwnd, 1, "regionview" ); popup_link( GTK_WIDGET( regionview ), regionview_popup_menu, ip ); regionview->dash_crawl = g_timeout_add( 200, (GSourceFunc) regionview_dash_crawl_cb, regionview ); } Regionview * regionview_new( Classmodel *classmodel, Rect *model_area, Imagepresent *ip ) { Regionview *regionview = gtk_type_new( TYPE_REGIONVIEW ); regionview_setup( regionview, classmodel, model_area, ip ); #ifdef DEBUG_MAKE printf( "regionview_new: %dx%d size %dx%d\n", model_area->left, model_area->top, model_area->width, model_area->height ); #endif /*DEBUG_MAKE*/ return( regionview ); } /* Type we display for each of the classes. Order is important! */ typedef struct { const char *name; RegionviewType type; } RegionviewDisplay; static RegionviewDisplay regionview_display_table[] = { { CLASS_HGUIDE, REGIONVIEW_HGUIDE }, { CLASS_VGUIDE, REGIONVIEW_VGUIDE }, { CLASS_MARK, REGIONVIEW_MARK }, { CLASS_AREA, REGIONVIEW_AREA }, { CLASS_REGION, REGIONVIEW_REGION }, { CLASS_ARROW, REGIONVIEW_ARROW } }; /* Look at the class we are drawing, set the display type. */ void regionview_set_type( Regionview *regionview, PElement *root ) { gboolean result; int i; if( heap_is_class( root, &result ) && result ) for( i = 0; i < IM_NUMBER( regionview_display_table ); i++ ) { const char *name = regionview_display_table[i].name; if( !heap_is_instanceof( name, root, &result ) ) continue; if( result ) { regionview->type = regionview_display_table[i].type; vobject_refresh_queue( VOBJECT( regionview ) ); break; } } } ================================================ FILE: src/regionview.h ================================================ /* draw a view of a region in an imageview */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_REGIONVIEW (regionview_get_type()) #define REGIONVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_REGIONVIEW, Regionview )) #define REGIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_REGIONVIEW, RegionviewClass )) #define IS_REGIONVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_REGIONVIEW )) #define IS_REGIONVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_REGIONVIEW )) #define REGIONVIEW_LABEL_MAX (256) /* States for the region view. */ typedef enum { REGIONVIEW_WAIT, /* Waiting for left down */ REGIONVIEW_MOVE, /* Dragging on label */ REGIONVIEW_RESIZE /* Dragging on resize handle */ } RegionviewState; /* Draw types. */ typedef enum { REGIONVIEW_REGION, /* width & height > 0 */ REGIONVIEW_AREA, /* width & height > 0 and locked */ REGIONVIEW_MARK, /* width & height == 0 */ REGIONVIEW_ARROW, /* width & height unconstrained */ REGIONVIEW_HGUIDE, /* width == image width, height == 0 */ REGIONVIEW_VGUIDE, /* width == 0, height == image height */ REGIONVIEW_LINE, /* floating dashed line for paintbox */ REGIONVIEW_BOX /* floating dashed box for paintbox */ } RegionviewType; /* Resize types. */ typedef enum { REGIONVIEW_RESIZE_NONE, REGIONVIEW_RESIZE_MOVE, REGIONVIEW_RESIZE_EDIT, REGIONVIEW_RESIZE_TOPLEFT, REGIONVIEW_RESIZE_TOP, REGIONVIEW_RESIZE_TOPRIGHT, REGIONVIEW_RESIZE_RIGHT, REGIONVIEW_RESIZE_BOTTOMRIGHT, REGIONVIEW_RESIZE_BOTTOM, REGIONVIEW_RESIZE_BOTTOMLEFT, REGIONVIEW_RESIZE_LEFT, REGIONVIEW_RESIZE_LAST } RegionviewResize; struct _Regionview { View view; RegionviewType type; gboolean frozen; /* type is frozen ... not rethought on resize */ /* State for resize/move etc. */ RegionviewState state; RegionviewResize resize;/* Resize type */ int dx, dy; /* Drag offset */ gboolean grabbed; /* Currently tracking with mouse */ /* The model we show. */ Classmodel *classmodel; Rect *model_area; /* What we read/write to talk to the model */ Rect our_area; /* Same, but our copy ... origin top left */ /* The imagepresent we draw on. */ Imagepresent *ip; iWindowCursorContext *cntxt; /* The signals we've connected to. */ guint expose_sid; guint destroy_sid; guint event_sid; guint changed_sid; guint conv_destroy_sid; guint model_changed_sid; /* Model info we read for display. */ GtkStateType paint_state;/* prelight/normal/etc. */ /* What's on the screen. */ gboolean unpainting; /* We are unpainting */ Rect area; /* Area of region ... image coordinates */ Rect label; /* Area covered by label ... canvas cods */ int ascent; /* Height of ascenders for text */ int dash_offset; guint dash_crawl; /* Timer for dash crawl animation */ GtkStateType last_paint_state; RegionviewType last_type; gboolean first; /* Initial draw (no old pos to remove) */ gboolean label_geo; /* Redo the label geo on refresh, please */ /* Text of label we display */ VipsBuf caption; }; typedef struct _RegionviewClass { ViewClass parent_class; /* My methods. */ } RegionviewClass; void regionview_attach( Regionview *regionview, int x, int y ); GtkType regionview_get_type( void ); Regionview *regionview_new( Classmodel *classmodel, Rect *model_area, Imagepresent *ip ); void regionview_set_type( Regionview *regionview, PElement *root ); ================================================ FILE: src/rhs.c ================================================ /* the rhs of a tallyrow ... group together everything to the right of the * button */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static HeapmodelClass *parent_class = NULL; /* child is about to be added ... update our graphic/scol/text shortcuts. */ static void rhs_child_add( iContainer *parent, iContainer *child, int pos ) { Rhs *rhs = RHS( parent ); if( IS_SUBCOLUMN( child ) ) { IDESTROY( rhs->scol ); rhs->scol = MODEL( child ); } else if( IS_ITEXT( child ) ) { IDESTROY( rhs->itext ); rhs->itext = MODEL( child ); } else { IDESTROY( rhs->graphic ); rhs->graphic = MODEL( child ); } ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos ); } static void rhs_child_remove( iContainer *parent, iContainer *child ) { Rhs *rhs = RHS( parent ); if( (void *) child == (void *) rhs->graphic ) rhs->graphic = NULL; else if( (void *) child == (void *) rhs->scol ) rhs->scol = NULL; else if( (void *) child == (void *) rhs->itext ) rhs->itext = NULL; ICONTAINER_CLASS( parent_class )->child_remove( parent, child ); } static void rhs_parent_add( iContainer *child ) { g_assert( IS_ROW( child->parent ) ); ICONTAINER_CLASS( parent_class )->parent_add( child ); } static View * rhs_view_new( Model *model, View *parent ) { return( rhsview_new() ); } static gboolean rhs_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { Rhs *rhs = RHS( model ); g_assert( IS_ROW( parent ) ); /* Hmm. Is this guaranteed? */ g_assert( sizeof( RhsFlags ) == sizeof( int ) ); if( !get_iprop( xnode, "vislevel", &rhs->vislevel ) || !get_iprop( xnode, "flags", (int *) &rhs->flags ) ) return( FALSE ); if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ) return( FALSE ); return( TRUE ); } static xmlNode * rhs_save( Model *model, xmlNode *xnode ) { Rhs *rhs = RHS( model ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( !set_iprop( xthis, "vislevel", rhs->vislevel ) || !set_iprop( xthis, "flags", rhs->flags ) ) return( NULL ); return( xthis ); } /* How to spot and make a graphic display. */ typedef struct { const char *name; GType (*type)( void ); } RhsGraphic; /* All our graphicdisplay widgets. Order is important! Most-derived classes * first. */ static RhsGraphic rhs_graphic[] = { { CLASS_CLOCK, clock_get_type }, { CLASS_EXPRESSION, expression_get_type }, { CLASS_GROUP, group_get_type }, { CLASS_LIST, group_get_type }, { CLASS_PATHNAME, pathname_get_type }, { CLASS_FONTNAME, fontname_get_type }, { CLASS_TOGGLE, toggle_get_type }, { CLASS_SLIDER, slider_get_type }, { CLASS_COLOUR, colour_get_type }, { CLASS_OPTION, option_get_type }, { CLASS_MATRIX, matrix_get_type }, { CLASS_ARROW, iarrow_get_type }, { CLASS_REGION, iregion_get_type }, { CLASS_PLOT, plot_get_type }, { CLASS_IMAGE, iimage_get_type }, { CLASS_NUMBER, number_get_type }, { CLASS_REAL, real_get_type }, { CLASS_VECTOR, vector_get_type }, { CLASS_STRING, string_get_type } }; /* Create/destroy the graphic display. */ static gboolean rhs_refresh_graphic( Rhs *rhs, PElement *root ) { gboolean result; Row *row = HEAPMODEL( rhs )->row; int i; if( !heap_is_class( root, &result ) ) return( FALSE ); /* Only for non-parameter class objects. */ if( result && row->sym && row->sym->type != SYM_PARAM ) { for( i = 0; i < IM_NUMBER( rhs_graphic ); i++ ) { const char *name = rhs_graphic[i].name; if( !heap_is_instanceof( name, root, &result ) ) return( FALSE ); if( result ) break; } if( i != IM_NUMBER( rhs_graphic ) ) { GType type = rhs_graphic[i].type(); if( !rhs->graphic || !TYPE_EXACT( rhs->graphic, type ) ) classmodel_new_classmodel( type, rhs ); } else /* Not a class we know about. */ IDESTROY( rhs->graphic ); } else /* Should be no graphic display. */ IDESTROY( rhs->graphic ); return( TRUE ); } static void * rhs_new_heap( Heapmodel *heapmodel, PElement *root ) { gboolean result; Rhs *rhs = RHS( heapmodel ); Row *row = HEAPMODEL( rhs )->row; #ifdef DEBUG printf( "rhs_new_heap: " ); row_name_print( HEAPMODEL( rhs )->row ); printf( "\n" ); #endif /*DEBUG*/ /* Create/reuse/destroy the graphic display. */ if( !rhs_refresh_graphic( rhs, root ) ) return( rhs ); /* Create/reuse/destroy class display. Only for non-param symbols. */ if( !heap_is_class( root, &result ) ) return( rhs ); if( result && row->sym && row->sym->type != SYM_PARAM ) { if( !rhs->scol || !IS_SUBCOLUMN( rhs->scol ) ) subcolumn_new( rhs, NULL ); } else /* Should be no klass display. */ IDESTROY( rhs->scol ); /* Create/reuse/destroy text display. */ if( !rhs->itext ) itext_new( rhs ); /* Recurse for children. */ if( rhs->graphic ) if( heapmodel_new_heap( HEAPMODEL( rhs->graphic ), root ) ) return( rhs ); if( rhs->scol ) if( heapmodel_new_heap( HEAPMODEL( rhs->scol ), root ) ) return( rhs ); if( rhs->itext ) if( heapmodel_new_heap( HEAPMODEL( rhs->itext ), root ) ) return( rhs ); return( HEAPMODEL_CLASS( parent_class )->new_heap( heapmodel, root ) ); } /* Rethink child visibility. */ void rhs_set_vislevel( Rhs *rhs, int vislevel ) { vislevel = IM_MAX( 0, vislevel ); #ifdef DEBUG printf( "rhs_set_vislevel: %d ...\n", vislevel ); #endif /*DEBUG*/ if( rhs->scol ) { Subcolumn *scol = SUBCOLUMN( rhs->scol ); if( rhs->graphic ) { switch( vislevel ) { case 0: rhs->flags = RHS_ITEXT; break; case 1: rhs->flags = RHS_GRAPHIC; break; case 2: rhs->flags = RHS_ITEXT | RHS_GRAPHIC; break; default: rhs->flags = RHS_ITEXT | RHS_GRAPHIC | RHS_SCOL; } subcolumn_set_vislevel( scol, vislevel - 2 ); if( vislevel < 3 ) rhs->vislevel = vislevel; else rhs->vislevel = scol->vislevel + 2; } else { vislevel = IM_MAX( 1, vislevel ); if( vislevel == 1 ) rhs->flags = RHS_ITEXT; else rhs->flags = RHS_ITEXT | RHS_SCOL; subcolumn_set_vislevel( scol, vislevel - 1 ); rhs->vislevel = scol->vislevel + 1; } } else { rhs->flags = RHS_ITEXT; rhs->vislevel = vislevel; } #ifdef DEBUG printf( "... set to: %d\n", rhs->vislevel ); #endif /*DEBUG*/ iobject_changed( IOBJECT( rhs ) ); } void rhs_vislevel_up( Rhs *rhs ) { rhs_set_vislevel( rhs, rhs->vislevel + 1 ); } void rhs_vislevel_down( Rhs *rhs ) { rhs_set_vislevel( rhs, rhs->vislevel - 1 ); } static void * rhs_update_model( Heapmodel *heapmodel ) { Rhs *rhs = RHS( heapmodel ); /* Update visibility. */ rhs_set_vislevel( rhs, rhs->vislevel ); return( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ); } static void rhs_class_init( RhsClass *class ) { iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ icontainer_class->child_add = rhs_child_add; icontainer_class->child_remove = rhs_child_remove; icontainer_class->parent_add = rhs_parent_add; model_class->view_new = rhs_view_new; model_class->load = rhs_load; model_class->save = rhs_save; heapmodel_class->new_heap = rhs_new_heap; heapmodel_class->update_model = rhs_update_model; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void rhs_init( Rhs *rhs ) { #ifdef DEBUG printf( "rhs_init\n" ); #endif /*DEBUG*/ /* -1 means not set yet ... default vislevel set by row_new_heap() * when the class members become available. */ rhs->vislevel = -1; rhs->graphic = NULL; rhs->scol = NULL; rhs->itext = NULL; } GType rhs_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( RhsClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) rhs_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Rhs ), 32, /* n_preallocs */ (GInstanceInitFunc) rhs_init, }; type = g_type_register_static( TYPE_HEAPMODEL, "Rhs", &info, 0 ); } return( type ); } Rhs * rhs_new( Row *row ) { Rhs *rhs = RHS( g_object_new( TYPE_RHS, NULL ) ); icontainer_child_add( ICONTAINER( row ), ICONTAINER( rhs ), -1 ); #ifdef DEBUG printf( "rhs_new: " ); row_name_print( HEAPMODEL( rhs )->row ); printf( " (%p)\n", rhs ); #endif /*DEBUG*/ return( rhs ); } static void * rhs_child_edited_sub( Model *model ) { Row *row = ROW( model ); if( row->child_rhs && rhs_child_edited( row->child_rhs ) ) return( row ); return( NULL ); } /* Does this RHS have any edited children? text, graphic, or recursive. */ gboolean rhs_child_edited( Rhs *rhs ) { if( rhs->itext && ITEXT( rhs->itext )->edited ) return( TRUE ); else if( rhs->graphic && CLASSMODEL( rhs->graphic )->edited ) return( TRUE ); else if( rhs->scol ) return( icontainer_map( ICONTAINER( rhs->scol ), (icontainer_map_fn) rhs_child_edited_sub, NULL, NULL ) != NULL ); else return( FALSE ); } ================================================ FILE: src/rhs.h ================================================ /* the rhs of a row ... group together everything to the right of the * button */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_RHS (rhs_get_type()) #define RHS( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_RHS, Rhs )) #define RHS_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_RHS, RhsClass)) #define IS_RHS( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_RHS )) #define IS_RHS_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_RHS )) #define RHS_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_RHS, RhsClass )) /* Which children are visible. */ typedef enum { RHS_GRAPHIC = 1, /* Graphical display */ RHS_SCOL = 2, /* Class browser display */ RHS_ITEXT = 4 /* Textual display */ } RhsFlags; struct _Rhs { Heapmodel parent_class; int vislevel; /* Visibility level */ RhsFlags flags; /* Which children we want visible */ Model *graphic; /* Graphic display ... toggle/slider/etc */ Model *scol; /* Class display */ Model *itext; /* Text display */ }; typedef struct _RhsClass { HeapmodelClass parent_class; /* My methods. */ } RhsClass; GType rhs_get_type( void ); Rhs *rhs_new( Row *row ); void rhs_set_vislevel( Rhs *rhs, int vislevel ); void rhs_vislevel_up( Rhs *rhs ); void rhs_vislevel_down( Rhs *rhs ); gboolean rhs_child_edited( Rhs *rhs ); ================================================ FILE: src/rhsview.c ================================================ /* the rhs of a tallyrow ... group together everything to the right of the * button */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; /* Get this if ws->mode changes. */ static void rhsview_reset( View *view ) { Rhsview *rhsview = RHSVIEW( view ); Rhs *rhs = RHS( VOBJECT( rhsview )->iobject ); Row *row = HEAPMODEL( rhs )->row; model_display( rhs->itext, row->ws->mode == WORKSPACE_MODE_FORMULA || rhs->flags & RHS_ITEXT ); VIEW_CLASS( parent_class )->reset( view ); } static void rhsview_refresh( vObject *vobject ) { Rhsview *rhsview = RHSVIEW( vobject ); Rhs *rhs = RHS( VOBJECT( rhsview )->iobject ); Row *row = HEAPMODEL( rhs )->row; #ifdef DEBUG printf( "rhsview_refresh: " ); row_name_print( HEAPMODEL( rhs )->row ); printf( " " ); if( rhs->flags & RHS_GRAPHIC ) printf( "RHS_GRAPHIC " ); if( rhs->flags & RHS_SCOL ) printf( "RHS_SCOL " ); if( rhs->flags & RHS_ITEXT ) printf( "RHS_ITEXT " ); printf( "\n" ); #endif /*DEBUG*/ /* Add/remove children according to rhs->flags. */ model_display( rhs->graphic, rhs->flags & RHS_GRAPHIC ); model_display( rhs->scol, rhs->flags & RHS_SCOL ); switch( row->ws->mode ) { case WORKSPACE_MODE_REGULAR: model_display( rhs->itext, rhs->flags & RHS_ITEXT ); break; case WORKSPACE_MODE_FORMULA: model_display( rhs->itext, TRUE ); break; case WORKSPACE_MODE_NOEDIT: /* Only show the text if it's the only this we have for this * row. */ if( rhs->graphic && rhs->flags & RHS_GRAPHIC ) model_display( rhs->itext, FALSE ); else if( rhs->scol && rhs->flags & RHS_SCOL ) model_display( rhs->itext, FALSE ); else model_display( rhs->itext, rhs->flags & RHS_ITEXT ); break; default: g_assert( 0 ); } VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void rhsview_link( View *view, Model *model, View *parent ) { Rhsview *rhsview = RHSVIEW( view ); Rowview *rview = ROWVIEW( parent ); #ifdef DEBUG printf( "rhsview_link: " ); row_name_print( ROW( VOBJECT( rview )->iobject ) ); printf( "\n" ); #endif /*DEBUG*/ VIEW_CLASS( parent_class )->link( view, model, parent ); rhsview->rview = rview; } static void rhsview_child_add( View *parent, View *child ) { Rhsview *rhsview = RHSVIEW( parent ); if( IS_SUBCOLUMNVIEW( child ) ) { gtk_table_attach_defaults( GTK_TABLE( rhsview->table ), GTK_WIDGET( child ), 0, 1, 1, 2 ); rhsview->scol = child; } else if( IS_ITEXTVIEW( child ) ) { gtk_table_attach_defaults( GTK_TABLE( rhsview->table ), GTK_WIDGET( child ), 0, 1, 2, 3 ); rhsview->itext = child; } else { gtk_table_attach_defaults( GTK_TABLE( rhsview->table ), GTK_WIDGET( child ), 0, 1, 0, 1 ); rhsview->graphic = child; g_assert( IS_GRAPHICVIEW( child ) ); } VIEW_CLASS( parent_class )->child_add( parent, child ); } static void rhsview_child_remove( View *parent, View *child ) { Rhsview *rhsview = RHSVIEW( parent ); if( IS_SUBCOLUMNVIEW( child ) ) rhsview->scol = NULL; else if( IS_ITEXTVIEW( child ) ) rhsview->itext = NULL; else rhsview->graphic = NULL; VIEW_CLASS( parent_class )->child_remove( parent, child ); } static void rhsview_class_init( RhsviewClass *class ) { vObjectClass *vobject_class = (vObjectClass*) class; ViewClass *view_class = (ViewClass*) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = rhsview_refresh; view_class->link = rhsview_link; view_class->child_add = rhsview_child_add; view_class->child_remove = rhsview_child_remove; view_class->reset = rhsview_reset; } static void rhsview_init( Rhsview *rhsview ) { rhsview->rview = NULL; /* Attached on refresh. */ rhsview->graphic = NULL; rhsview->scol = NULL; rhsview->itext = NULL; rhsview->table = gtk_table_new( 3, 1, FALSE ); gtk_box_pack_start( GTK_BOX( rhsview ), rhsview->table, TRUE, FALSE, 0 ); gtk_widget_show( rhsview->table ); rhsview->flags = 0; gtk_widget_show( GTK_WIDGET( rhsview ) ); } GtkType rhsview_get_type( void ) { static GtkType rhsview_type = 0; if( !rhsview_type ) { static const GtkTypeInfo rhsview_info = { "Rhsview", sizeof( Rhsview ), sizeof( RhsviewClass ), (GtkClassInitFunc) rhsview_class_init, (GtkObjectInitFunc) rhsview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; rhsview_type = gtk_type_unique( TYPE_VIEW, &rhsview_info ); } return( rhsview_type ); } View * rhsview_new( void ) { Rhsview *rhsview = gtk_type_new( TYPE_RHSVIEW ); return( VIEW( rhsview ) ); } ================================================ FILE: src/rhsview.h ================================================ /* the rhs of a tallyrow ... group together everything to the right of the * button */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_RHSVIEW (rhsview_get_type()) #define RHSVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_RHSVIEW, Rhsview )) #define RHSVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_RHSVIEW, RhsviewClass )) #define IS_RHSVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_RHSVIEW )) #define IS_RHSVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_RHSVIEW )) struct _Rhsview { View item; Rowview *rview; View *graphic; /* Our three elements */ View *scol; View *itext; GtkWidget *table; /* Lay out elements in this */ RhsFlags flags; /* Last vis set we set */ }; typedef struct _RhsviewClass { ViewClass parent_class; /* My methods. */ } RhsviewClass; GtkType rhsview_get_type( void ); View *rhsview_new( void ); ================================================ FILE: src/row.c ================================================ /* A row in a workspace ... not a widget, part of subcolumn */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Mad detail. #define DEBUG */ /* Show each row being calculated. #define DEBUG_ROW */ /* Making and removing links between rows. #define DEBUG_LINK */ /* Time row recomp. #define DEBUG_TIME_SORT */ /* Trace create/destroy. #define DEBUG_NEW */ /* Dirty/clean stuff. #define DEBUG_DIRTY */ /* Error set/clear. #define DEBUG_ERROR */ /* Show row recomp order decisions. #define DEBUG_SORT_VERBOSE #define DEBUG_SORT */ #include "ip.h" static HeapmodelClass *parent_class = NULL; static void * row_map_all_sub( Model *model, row_map_fn fn, void *a, void *b, void *c ) { if( IS_ROW( model ) ) return( fn( ROW( model ), a, b, c ) ); return( NULL ); } static void * row_map_all( Row *row, row_map_fn fn, void *a, void *b, void *c ) { return( icontainer_map4_all( ICONTAINER( row ), (icontainer_map4_fn) row_map_all_sub, (void *) fn, a, b, c ) ); } const char * row_name( Row *row ) { if( row->sym ) return( IOBJECT( row->sym )->name ); else return( IOBJECT( row )->name ); } static Row * row_get_parent( Row *row ) { return( HEAPMODEL( row )->row ); } /* Make a fully-qualified name for a row's symbol ... walk back up the tally * hierarchy. eg. "A1.fred.x" ... produce a name which will find this row from * a local of context. */ void row_qualified_name_relative( Symbol *context, Row *row, VipsBuf *buf ) { if( !row_get_parent( row ) ) { if( !row->sym ) vips_buf_appends( buf, "(null)" ); else symbol_qualified_name_relative( context, row->sym, buf ); } else { /* Qualify our parents, then do us. */ row_qualified_name_relative( context, row_get_parent( row ), buf ); vips_buf_appends( buf, "." ); vips_buf_appends( buf, row_name( row ) ); } } /* Make a fully-qualified name for a row's symbol ... walk back up the tally * hierarchy. eg. "A1.fred.x". */ void row_qualified_name( Row *row, VipsBuf *buf ) { if( row->ws ) row_qualified_name_relative( row->ws->sym, row, buf ); } /* Convenience ... print a row name out, identifying by tally heirarchy. */ void * row_name_print( Row *row ) { if( row ) { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); row_qualified_name( row, &buf ); printf( "%s ", vips_buf_all( &buf ) ); } else printf( "(null)" ); return( NULL ); } static void * row_dirty_clear( Row *row ) { #ifdef DEBUG_DIRTY { Row *top_row = row->top_row; if( row->dirty ) g_assert( g_slist_find( top_row->recomp, row ) ); } #endif /*DEBUG_DIRTY*/ if( row->dirty ) { Row *top_row = row->top_row; row->dirty = FALSE; top_row->recomp = g_slist_remove( top_row->recomp, row ); #ifdef DEBUG_DIRTY printf( "row_dirty_clear: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG_DIRTY*/ iobject_changed( IOBJECT( row ) ); } return( NULL ); } /* Set a single row dirty. */ static void * row_dirty_set_single( Row *row, gboolean clear_error ) { #ifdef DEBUG_DIRTY { Row *top_row = row->top_row; if( row->dirty ) g_assert( g_slist_find( top_row->recomp, row ) ); if( !row->dirty ) g_assert( !g_slist_find( top_row->recomp, row ) ); } #endif /*DEBUG_DIRTY*/ if( !row->dirty ) { Row *top_row = row->top_row; row->dirty = TRUE; top_row->recomp = g_slist_prepend( top_row->recomp, row ); iobject_changed( IOBJECT( row ) ); #ifdef DEBUG_DIRTY printf( "row_dirty_set_single: " ); row_name_print( row ); printf( " clear_error = %s\n", bool_to_char( clear_error ) ); #endif /*DEBUG_DIRTY*/ /* Make sure error is clear ... we want to recomp. */ if( row->expr && clear_error ) expr_error_clear( row->expr ); } return( NULL ); } static void * row_dirty_set_sub( Model *model, gboolean clear_error ) { if( IS_ROW( model ) ) { Row *row = ROW( model ); Rhs *rhs = row->child_rhs; g_assert( !rhs || IS_RHS( rhs ) ); if( rhs && rhs->itext && ITEXT( rhs->itext )->edited ) row_dirty_set_single( row, clear_error ); else if( rhs && rhs->graphic && CLASSMODEL( rhs->graphic )->edited ) row_dirty_set_single( row, clear_error ); } return( NULL ); } /* When we mark a row dirty, we need to mark any subrows with non-default * values dirty too so that they will get a chance to reapply their edits over * the top of the new value we will make for this row. */ static void * row_dirty_set( Row *row, gboolean clear_error ) { row_dirty_set_single( row, clear_error ); return( icontainer_map_all( ICONTAINER( row ), (icontainer_map_fn) row_dirty_set_sub, GINT_TO_POINTER( clear_error ) ) ); } /* Mark a row as containing an error ... called from expr_error_set() * ... don't call this directly. */ void row_error_set( Row *row ) { if( !row->err ) { Workspace *ws = row->ws; gboolean was_clear = ws->errors == NULL; ws->errors = g_slist_prepend( ws->errors, row ); row->err = TRUE; #ifdef DEBUG_ERROR printf( "row_error_set: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG_ERROR*/ iobject_changed( IOBJECT( row ) ); /* First error? State change on workspace. */ if( was_clear ) iobject_changed( IOBJECT( ws ) ); /* If this is a local row, mark the top row in error too to end * recomp on this tree. */ if( row != row->top_row ) { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); row_qualified_name( row, &buf ); error_top( _( "Error in row." ) ); /* Elements are name of row, principal error, * secondary error. */ error_sub( _( "Error in row %s: %s\n%s" ), vips_buf_all( &buf ), row->expr->error_top, row->expr->error_sub ); expr_error_set( row->top_row->expr ); } } } /* Clear error state ... called from expr_error_clear() ... don't call this * directly. */ void row_error_clear( Row *row ) { if( row->err ) { Workspace *ws = row->ws; ws->errors = g_slist_remove( ws->errors, row ); row->err = FALSE; iobject_changed( IOBJECT( row ) ); #ifdef DEBUG_ERROR printf( "row_error_clear: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG_ERROR*/ /* Mark our text modified to make sure we reparse and compile. * The code may contain pointers to dead symbols if we were in * error because they were undefined. */ if( row->child_rhs && row->child_rhs->itext ) heapmodel_set_modified( HEAPMODEL( row->child_rhs->itext ), TRUE ); /* All errors gone? Ws changed too. */ if( !ws->errors ) iobject_changed( IOBJECT( ws ) ); /* Is this a local row? Clear the top row error as well, in * case it's in error because of us. */ if( row != row->top_row && row->top_row->expr ) { expr_error_clear( row->top_row->expr ); row_dirty_set( row->top_row, TRUE ); } } } /* Break a dependency. */ static void * row_link_break( Row *parent, Row *child ) { /* Must be there. */ g_assert( g_slist_find( parent->children, child ) && g_slist_find( child->parents, parent ) ); parent->children = g_slist_remove( parent->children, child ); child->parents = g_slist_remove( child->parents, parent ); #ifdef DEBUG_LINK printf( "row_link_break: breaking link from " ); row_name_print( parent ); printf( "to " ); row_name_print( child ); printf( "\n" ); #endif /*DEBUG_LINK*/ return( NULL ); } static void * row_link_break_rev( Row *child, Row *parent ) { return( row_link_break( parent, child ) ); } static void row_dispose( GObject *gobject ) { Row *row = ROW( gobject ); #ifdef DEBUG_NEW /* Can't use row_name_print(), we may not have a parent. */ printf( "row_dispose: %s", NN( IOBJECT( row )->name ) ); if( row->sym ) printf( " (%s)", symbol_name( row->sym ) ); printf( "\n" ); #endif /*DEBUG_NEW*/ /* Reset state. Also see row_parent_remove(). */ row_hide_dependents( row ); if( row->expr ) expr_error_clear( row->expr ); if( row->top_col && row->top_col->last_select == row ) row->top_col->last_select = NULL; row_deselect( row ); /* Break all recomp links. */ slist_map( row->parents, (SListMapFn) row_link_break, row ); slist_map( row->children, (SListMapFn) row_link_break_rev, row ); g_assert( !row->parents && !row->children ); (void) slist_map( row->recomp, (SListMapFn) row_dirty_clear, NULL ); if( row->top_row ) row->top_row->recomp_save = g_slist_remove( row->top_row->recomp_save, row ); IM_FREEF( g_slist_free, row->recomp_save ); g_assert( !row->recomp ); if( row->expr ) { g_assert( row->expr->row == row ); /* If we're a local row, we will have a private expr * allocated for us. Junk it. */ if( row != row->top_row ) icontainer_child_remove( ICONTAINER( row->expr ) ); else { /* Top-level row, we were zapping the sym's expr. * Break the link to it. */ row->expr->row = NULL; row->expr = NULL; } } /* Is this a top-level row? Kill the symbol too. Need to do this after * sorting out row->expr, since otherwise killing the symbol will kill * us again in turn. */ if( row == row->top_row ) IDESTROY( row->sym ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void * row_add_parent_name( Link *link, VipsBuf *buf ) { Row *row; if( link->parent->expr && (row = link->parent->expr->row) ) { row_qualified_name_relative( link->child, row, buf ); vips_buf_appends( buf, " " ); } return( NULL ); } static void * row_add_child_name( Link *link, VipsBuf *buf ) { Row *row; if( link->child->expr && (row = link->child->expr->row) ) { row_qualified_name_relative( link->parent, row, buf ); vips_buf_appends( buf, " " ); } return( NULL ); } static void * row_add_dirty_child_name( Link *link, VipsBuf *buf ) { if( link->child->dirty ) { symbol_qualified_name_relative( link->parent, link->child, buf ); vips_buf_appends( buf, " " ); } return( NULL ); } static void row_info( iObject *iobject, VipsBuf *buf ) { Row *row = ROW( iobject ); vips_buf_appends( buf, _( "Name" ) ); vips_buf_appends( buf, ": " ); row_qualified_name( row, buf ); vips_buf_appends( buf, "\n" ); if( row->expr ) iobject_info( IOBJECT( row->expr ), buf ); if( row->child_rhs && row->child_rhs->itext ) iobject_info( IOBJECT( row->child_rhs->itext ), buf ); if( row->child_rhs && row->child_rhs->graphic ) iobject_info( IOBJECT( row->child_rhs->graphic ), buf ); if( row->top_row->sym ) { if( row->top_row->sym->topchildren ) { row_qualified_name( row, buf ); vips_buf_appends( buf, " " ); /* Expands to eg. "B1 refers to: B2, B3". */ vips_buf_appends( buf, _( "refers to" ) ); vips_buf_appends( buf, ": " ); slist_map_rev( row->top_row->sym->topchildren, (SListMapFn) row_add_child_name, buf ); vips_buf_appends( buf, "\n" ); } if( row->top_row->sym->topparents ) { row_qualified_name( row, buf ); vips_buf_appends( buf, " " ); /* Expands to eg. "B1 is referred to by: B2, B3". */ vips_buf_appends( buf, _( "is referred to by" ) ); vips_buf_appends( buf, ": " ); slist_map_rev( row->top_row->sym->topparents, (SListMapFn) row_add_parent_name, buf ); vips_buf_appends( buf, "\n" ); } } if( row == row->top_row && row->sym && row->sym->dirty ) { Symbol *sym = row->sym; if( sym->ndirtychildren ) { row_qualified_name( row, buf ); vips_buf_appends( buf, " " ); vips_buf_appends( buf, _( "is blocked on" ) ); vips_buf_appends( buf, ": " ); slist_map_rev( sym->topchildren, (SListMapFn) row_add_dirty_child_name, buf ); vips_buf_appends( buf, "\n" ); } } } static Rhs * row_get_rhs( Row *row ) { g_assert( g_slist_length( ICONTAINER( row )->children ) == 1 ); return( RHS( ICONTAINER( row )->children->data ) ); } static void row_child_add( iContainer *parent, iContainer *child, int pos ) { Row *row = ROW( parent ); ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos ); /* Update our context. */ row->child_rhs = row_get_rhs( row ); } static Subcolumn * row_get_subcolumn( Row *row ) { return( SUBCOLUMN( ICONTAINER( row )->parent ) ); } static Column * row_get_column( Row *row ) { Subcolumn *scol = row_get_subcolumn( row ); if( scol ) return( scol->top_col ); else return( NULL ); } /* Search back up the widget hierarchy for the base row for this * row ... eg "A7"->expr->row. */ static Row * row_get_root( Row *row ) { Row *enclosing = row_get_parent( row ); if( !enclosing ) return( row ); else return( row_get_root( enclosing ) ); } Workspace * row_get_workspace( Row *row ) { Column *col = row_get_column( row ); if( col ) return( col->ws ); else return( NULL ); } static void row_parent_add( iContainer *child ) { Row *row = ROW( child ); g_assert( IS_SUBCOLUMN( child->parent ) ); ICONTAINER_CLASS( parent_class )->parent_add( child ); /* Update our context. */ row->scol = row_get_subcolumn( row ); row->top_col = row_get_column( row ); row->ws = row_get_workspace( row ); row->top_row = row_get_root( row ); } static void row_parent_remove( iContainer *child ) { Row *row = ROW( child ); /* Reset the parts of state which touch our parent. */ row_dirty_clear( row ); row_deselect( row ); /* Don't clear error ... we may no longer have the link to expr. See * row_dispose() for that. */ ICONTAINER_CLASS( parent_class )->parent_remove( child ); } static View * row_view_new( Model *model, View *parent ) { return( rowview_new() ); } static void row_scrollto( Model *model, ModelScrollPosition position ) { Row *row = ROW( model ); Column *col = row->top_col; /* If our column is closed, there won't be a view to scrollto, ouch! * Need to open the column first, then scroll to that column. We can't * scroll to the exact row, since there's no view for it, and won't be * for a while after we hit the idle loop again. */ if( !col->open ) { column_set_open( col, TRUE ); column_scrollto( col, position ); } MODEL_CLASS( parent_class )->scrollto( model, position ); } static gboolean row_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { Row *row = ROW( model ); Subcolumn *scol = SUBCOLUMN( parent ); char name[256]; g_assert( IS_SUBCOLUMN( parent ) ); if( !get_sprop( xnode, "name", name, 256 ) ) return( FALSE ); IM_SETSTR( IOBJECT( row )->name, name ); #ifdef DEBUG printf( "row_load: loading row %s (xmlNode %p)\n", name, xnode ); #endif /*DEBUG*/ /* Popup is optional (top level only) */ (void) get_bprop( xnode, "popup", &row->popup ); if( scol->is_top ) { Column *col = scol->top_col; Workspace *ws = col->ws; Symbol *sym; sym = symbol_new( ws->sym->expr->compile, name ); symbol_user_init( sym ); (void) compile_new_local( sym->expr ); row_link_symbol( row, sym, NULL ); /* We can't symbol_made() here, we've not parsed our value * yet. See below ... we just make sure we're on the recomp * lists. */ } if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ) return( FALSE ); /* If we've loaded a complete row system, mark this row plus any * edited subrows dirty, and make sure this sym is dirty too. */ if( scol->is_top ) { row_dirty_set( row, TRUE ); expr_dirty( row->sym->expr, link_serial_new() ); } return( TRUE ); } /* Should we display this row. Non-displayed rows don't have rhs, don't * appear on the screen, and aren't saved. They do have rows though, so their * dependencies are tracked. * * We work off sym rather than row so that we can work before the row is fully * built. */ static gboolean row_is_displayable( Symbol *sym ) { if( is_system( sym ) ) return( FALSE ); if( sym->expr && sym->expr->compile && sym->expr->compile->nparam > 0 ) return( FALSE ); if( is_super( sym ) && sym->expr ) { Expr *expr = sym->expr; PElement *root = &expr->root; /* Empty superclass. */ if( PEISELIST( root ) ) return( FALSE ); } return( TRUE ); } static xmlNode * row_save( Model *model, xmlNode *xnode ) { Row *row = ROW( model ); xmlNode *xthis; /* Don't save system rows, or empty superclasses. */ if( row->sym ) { if( !row_is_displayable( row->sym ) ) /* Need to return non-NULL for abort with no error. */ return( (xmlNode *) -1 ); } if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); /* Top-level only. */ if( row->top_row == row ) if( !set_sprop( xthis, "popup", bool_to_char( row->popup ) ) ) return( NULL ); if( !set_sprop( xthis, "name", IOBJECT( row )->name ) ) return( NULL ); return( xthis ); } static void * row_clear_to_save( Model *model ) { if( IS_ROW( model ) ) ROW( model )->to_save = FALSE; return( NULL ); } static void * row_set_to_save( Row *row ) { Row *enclosing; if( !row->to_save ) { row->to_save = TRUE; /* All peers must be saved. When we reload, we want to keep * row ordering. If we just save modded row, they'll move to * the front of the row list on reload, since they'll be made * first. */ icontainer_map( ICONTAINER( row->scol ), (icontainer_map_fn) row_set_to_save, NULL, NULL ); /* All rows back up to the top level must also be saved. */ for( enclosing = row; enclosing != row->top_row; enclosing = row_get_parent( enclosing ) ) row_set_to_save( enclosing ); } return( NULL ); } static void * row_calculate_to_save( Model *model ) { if( IS_ROW( model ) ) { Row *row = ROW( model ); Rhs *rhs = row->child_rhs; if( row != row->top_row && rhs && !row->to_save ) { if( rhs->itext && ITEXT( rhs->itext )->edited ) row_set_to_save( row ); else if( rhs->graphic && CLASSMODEL( rhs->graphic )->edited ) row_set_to_save( row ); } } return( NULL ); } static gboolean row_save_test( Model *model ) { Row *row = ROW( model ); Workspace *ws = row->ws; Workspacegroup *wsg = workspace_get_workspacegroup( ws ); gboolean save; if( row == row->top_row ) { /* This is a top-level row ... save unless we're in * only-save-selected mode. */ if( wsg->save_type == WORKSPACEGROUP_SAVE_SELECTED ) save = row->selected; else save = TRUE; /* If we're going to save this row, clear all the to_save * flags, then walk the tree working out which bits we will need * to write. */ if( save ) { icontainer_map_all( ICONTAINER( row ), (icontainer_map_fn) row_clear_to_save, NULL ); icontainer_map_all( ICONTAINER( row ), (icontainer_map_fn) row_calculate_to_save, NULL ); } } else save = row->to_save; return( save ); } static void * row_new_heap( Heapmodel *heapmodel, PElement *root ) { Row *row = ROW( heapmodel ); Expr *expr = row->expr; #ifdef DEBUG printf( "row_new_heap: " ); row_name_print( row ); printf( "\n" ); printf( "row_new_heap: new value is " ); pgraph( root ); printf( "row_new_heap: top value is " ); pgraph( &row->top_row->expr->root ); #endif /*DEBUG*/ if( row_is_displayable( row->sym ) ) { /* Hide superclasses whose constructor starts with "_". */ if( is_super( row->sym ) && PEISCLASS( root ) && *IOBJECT( PEGETCLASSCOMPILE( root )->sym )->name == '_' ) model_display( MODEL( row ), FALSE ); } /* New value ... reset error state. */ expr_error_clear( expr ); expr->root = *root; expr_new_value( expr ); if( row->child_rhs && heapmodel_new_heap( HEAPMODEL( row->child_rhs ), root ) ) return( row ); /* Class display only for non-param classes. */ row->is_class = PEISCLASS( root ) && row->sym->type != SYM_PARAM; /* Set the default vis level. */ if( row->child_rhs && row->child_rhs->vislevel == -1 ) { PElement member; double value; gboolean is_class; if( !heap_is_class( root, &is_class ) ) return( row ); /* If it's a class with a vis hint, use that. */ if( is_class && class_get_member( root, MEMBER_VISLEVEL, NULL, &member ) && heap_get_real( &member, &value ) ) rhs_set_vislevel( row->child_rhs, value ); /* Non-parameter rows get higher vislevel, except for super. */ else if( row->sym->type != SYM_PARAM && !is_super( row->sym ) ) rhs_set_vislevel( row->child_rhs, 1 ); else rhs_set_vislevel( row->child_rhs, 0 ); } return( HEAPMODEL_CLASS( parent_class )->new_heap( heapmodel, root ) ); } static void * row_update_model( Heapmodel *heapmodel ) { Row *row = ROW( heapmodel ); if( row->expr ) expr_new_value( row->expr ); return( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) ); } static void row_class_init( RowClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = row_dispose; iobject_class->info = row_info; icontainer_class->child_add = row_child_add; icontainer_class->parent_add = row_parent_add; icontainer_class->parent_remove = row_parent_remove; model_class->view_new = row_view_new; model_class->scrollto = row_scrollto; model_class->load = row_load; model_class->save = row_save; model_class->save_test = row_save_test; heapmodel_class->new_heap = row_new_heap; heapmodel_class->update_model = row_update_model; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void row_init( Row *row ) { #ifdef DEBUG printf( "row_init\n" ); #endif /*DEBUG*/ row->scol = NULL; row->child_rhs = NULL; row->top_col = NULL; row->ws = NULL; row->top_row = NULL; row->sym = NULL; row->expr = NULL; row->err = FALSE; row->selected = FALSE; row->is_class = FALSE; row->popup = POPUP_NEW_ROWS; row->to_save = FALSE; /* Init recomp stuff. */ row->parents = NULL; row->children = NULL; row->dirty = FALSE; row->recomp = NULL; row->recomp_save = NULL; row->depend = FALSE; row->show = ROW_SHOW_NONE; } GType row_get_type( void ) { static GType row_type = 0; if( !row_type ) { static const GTypeInfo info = { sizeof( RowClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) row_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Row ), 32, /* n_preallocs */ (GInstanceInitFunc) row_init, }; row_type = g_type_register_static( TYPE_HEAPMODEL, "Row", &info, 0 ); } return( row_type ); } /* After making a row and adding it to model tree ... attach the symbol and * value this row displays. */ void row_link_symbol( Row *row, Symbol *sym, PElement *root ) { g_assert( !row->sym ); g_assert( !row->expr ); g_assert( !sym->expr || !sym->expr->row ); row->sym = sym; /* Code we display/update ... if this is a top-level row, we * directly change the symbol's expr. If it's a sub-row, we need a * cloned expr for us to fiddle with. */ if( is_top( sym ) ) { row->expr = sym->expr; g_assert( !row->expr->row ); row->expr->row = row; } else { row->expr = expr_clone( sym ); row->expr->row = row; if( root ) { row->expr->root = *root; expr_new_value( row->expr ); } } } Row * row_new( Subcolumn *scol, Symbol *sym, PElement *root ) { Row *row = g_object_new( TYPE_ROW, NULL ); #ifdef DEBUG_NEW printf( "row_new: " ); dump_tiny( sym ); printf( "\n" ); #endif /*DEBUG_NEW*/ /* Don't make a display or a RHS for invisible rows. */ if( !row_is_displayable( sym ) ) MODEL( row )->display = FALSE; else (void) rhs_new( row ); iobject_set( IOBJECT( row ), IOBJECT( sym )->name, NULL ); icontainer_child_add( ICONTAINER( scol ), ICONTAINER( row ), -1 ); row_link_symbol( row, sym, root ); return( row ); } /* Make a dependency. parent is displaying an expression which * refers to the symbol being displayed by child. */ static void * row_link_make( Row *parent, Row *child ) { /* Already a dependency? Don't make a second link. */ if( g_slist_find( parent->children, child ) ) return( NULL ); /* Don't link to self (harmless, but pointless too). */ if( parent == child ) return( NULL ); /* New link, each direction. */ parent->children = g_slist_prepend( parent->children, child ); child->parents = g_slist_prepend( child->parents, parent ); #ifdef DEBUG_LINK printf( "row_link_make: " ); row_name_print( parent ); printf( "refers to " ); row_name_print( child ); printf( "\n" ); #endif /*DEBUG_LINK*/ return( NULL ); } static void * row_link_build4( Expr *child_expr, Row *row ) { if( child_expr->row && child_expr->row->top_row == row ) return( child_expr->row ); return( NULL ); } /* Does child have a display in the same tally heirarchy as row? Make a link! */ static void * row_link_build3( Symbol *child, Row *row ) { Row *child_row; child_row = (Row *) icontainer_map( ICONTAINER( child ), (icontainer_map_fn) row_link_build4, row->top_row, NULL ); if( child_row ) (void) row_link_make( row, child_row ); return( NULL ); } static void *row_link_build2( Expr *expr, Row *row ); static void * row_link_build2_sym( Symbol *sym, Row *row ) { if( sym->expr && row_link_build2( sym->expr, row ) ) return( row ); return( NULL ); } static void * row_link_build2( Expr *expr, Row *row ) { /* Make links to anything expr refers to in this tree. */ if( expr->compile && slist_map( expr->compile->children, (SListMapFn) row_link_build3, row ) ) return( expr ); /* Recurse for any locals of expr. * Exception: * * f = class { * g = class { * a = 12; * } * } * * zero-arg local classes will have rows anyway, so we don't need to * check inside them for locals, since we'll do them anyway at the top * level. * * zero-arg hidden classes do need to be checked inside though :-( * since we will only have a row for the top element. */ if( expr->compile && !(is_class( expr->compile ) && expr->compile->nparam == 0 && expr->row && MODEL( expr->row )->display) && icontainer_map( ICONTAINER( expr->compile ), (icontainer_map_fn) row_link_build2_sym, row, NULL ) ) return( expr ); return( NULL ); } /* Scan a row, adding links for any dependencies we find. */ static void * row_link_build( Row *row ) { #ifdef DEBUG_LINK printf( "row_link_build: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG_LINK*/ /* Build new recomp list. Only for class displays. */ if( !row->scol->is_top && row->expr && row_link_build2( row->expr, row ) ) return( row ); return( NULL ); } /* Remove any links on a row. */ static void * row_link_destroy( Row *row ) { slist_map( row->children, (SListMapFn) row_link_break_rev, row ); return( NULL ); } static void *row_dependent_map_sub( Row *row, row_map_fn fn, void *a ); /* Do this row, and any that depend on it. */ static void * row_dependent_mark( Row *row, row_map_fn fn, void *a ) { void *res; /* Done this one already? */ if( row->depend ) return( NULL ); row->depend = TRUE; if( (res = fn( row, a, NULL, NULL )) ) return( res ); return( row_dependent_map_sub( row, fn, a ) ); } /* Apply to all dependents of row. */ static void * row_dependent_map_sub( Row *row, row_map_fn fn, void *a ) { Row *i; void *res; /* Things that refer to us. */ if( (res = slist_map2( row->parents, (SListMap2Fn) row_dependent_mark, (void *) fn, a )) ) return( res ); /* Things that refer to our enclosing syms ... eg. if A1.fred.x * changes, we don't want to recalc A1.fred, we do want to recalc * anything that refers to A1.fred. */ for( i = row; (i = HEAPMODEL( i )->row); ) if( (res = row_dependent_map_sub( i, fn, a )) ) return( res ); /* We are not going to spot things that refer to this.us :-( we could * say anything that depends on "this" depends on us, but that's much * too broad (and much too slow). FIXME ... could use dynamic dependency stuff to find things that refer to this.us? */ return( NULL ); } static void * row_dependent_clear( Row *row ) { row->depend = FALSE; return( NULL ); } /* Apply a function to all rows in this tree which depend on this row. */ void * row_dependent_map( Row *row, row_map_fn fn, void *a ) { /* Clear the flags we use to spot loops. */ row_map_all( row->top_row, (row_map_fn) row_dependent_clear, NULL, NULL, NULL ); return( row_dependent_map_sub( row, fn, a ) ); } /* This row has changed ... mark all dependents (direct and indirect) * dirty. */ void * row_dirty( Row *row, gboolean clear_error ) { (void) row_dirty_set( row, clear_error ); (void) row_dependent_map( row, (row_map_fn) row_dirty_set, GINT_TO_POINTER( clear_error ) ); return( NULL ); } /* This tally has changed ... mark all dependents (but not this one!) * dirty. */ void * row_dirty_intrans( Row *row, gboolean clear_error ) { (void) row_dependent_map( row, (row_map_fn) row_dirty_set, GINT_TO_POINTER( clear_error ) ); return( NULL ); } /* Find the 'depth' of a row ... 0 is top level. */ static int row_recomp_depth( Row *row ) { if( row == row->top_row ) return( 0 ); return( 1 + row_recomp_depth( row_get_parent( row ) ) ); } /* Compare func for row recomp sort. */ static int row_recomp_sort_func( Row *a, Row *b ) { int order; #ifdef DEBUG_TIME_SORT static GTimer *sort_func_timer = NULL; if( !sort_func_timer ) sort_func_timer = g_timer_new(); g_timer_reset( sort_func_timer ); #endif /*DEBUG_TIME_SORT*/ #ifdef DEBUG_SORT_VERBOSE printf( "row_recomp_sort_func: " ); #endif /*DEBUG_SORT_VERBOSE*/ /* If b depends on a, want a first. */ if( row_dependent_map( a, (row_map_fn) map_equal, b ) ) { #ifdef DEBUG_SORT_VERBOSE row_name_print( a ); printf( "before " ); row_name_print( b ); printf( "(2nd depends on 1st)\n" ); #endif /*DEBUG_SORT_VERBOSE*/ order = -1; } else if( row_dependent_map( b, (row_map_fn) map_equal, a ) ) { #ifdef DEBUG_SORT_VERBOSE row_name_print( b ); printf( "before " ); row_name_print( a ); printf( "(2nd depends on 1st #2)\n" ); #endif /*DEBUG_SORT_VERBOSE*/ order = 1; } else { int adepth = row_recomp_depth( a ); int bdepth = row_recomp_depth( b ); #ifdef DEBUG_SORT_VERBOSE if( adepth < bdepth ) { row_name_print( a ); printf( "before " ); row_name_print( b ); printf( "(1st shallower)\n" ); } else if( bdepth < adepth ) { row_name_print( b ); printf( "before " ); row_name_print( a ); printf( "(1st shallower)\n" ); } else { row_name_print( a ); printf( "and " ); row_name_print( b ); printf( "independent\n" ); } #endif /*DEBUG_SORT_VERBOSE*/ /* No dependency ... want shallower first. */ order = adepth - bdepth; } #ifdef DEBUG_TIME_SORT printf( "row_recomp_sort_func: took %gs\n", g_timer_elapsed( sort_func_timer, NULL ) ); #endif /*DEBUG_TIME_SORT*/ return( order ); } /* Insert-sort an slist. */ static GSList * row_recomp_sort_slist( GSList *old ) { GSList *new; GSList *p; new = NULL; for( p = old; p; p = p->next ) { Row *a = (Row *) p->data; Row *b; GSList *q; for( q = new; q; q = q->next ) { b = (Row *) q->data; if( row_recomp_sort_func( a, b ) < 0 ) break; } if( q ) { q->data = a; q->next = g_slist_prepend( q->next, b ); } else new = g_slist_append( new, a ); } g_slist_free( old ); return( new ); } /* Sort dirties into recomp order. */ static void row_recomp_sort( Row *row ) { #ifdef DEBUG_TIME_SORT static GTimer *sort_timer = NULL; if( !sort_timer ) sort_timer = g_timer_new(); g_timer_reset( sort_timer ); #endif /*DEBUG_TIME_SORT*/ g_assert( row == row->top_row ); /* Nope, can't use g_slist_sort(). We have a partial order and * g_slist_sort() uses an algorithm that assumes a full order. Do a * simple insert-sort, it'll do enough comparisons that we won't miss * things. row->recomp = g_slist_sort( row->recomp, (GCompareFunc) row_recomp_sort_func ); */ row->recomp = row_recomp_sort_slist( row->recomp ); #ifdef DEBUG_TIME_SORT printf( "row_recomp_sort: took %gs\n", g_timer_elapsed( sort_timer, NULL ) ); #endif /*DEBUG_TIME_SORT*/ #ifdef DEBUG_SORT printf( "row_recomp: sorted dirties are: " ); slist_map( row->recomp, (SListMapFn) row_name_print, NULL ); printf( "\n" ); #endif /*DEBUG_SORT*/ } static gboolean row_regenerate( Row *row ) { Expr *expr = row->expr; PElement base; /* Regenerate any compiled code. */ if( expr->compile ) { PEPOINTE( &base, &expr->compile->base ); if( !PEISNOVAL( &base ) ) { PElement *root = &expr->root; if( row == row->top_row ) { /* Recalcing base of tally display ... not a * class member, must be a sym with a value. */ gboolean res; res = reduce_regenerate( expr, root ); expr_new_value( expr ); if( !res ) return( FALSE ); } else { /* Recalcing a member somewhere inside ... * regen (member this) pair. Get the "this" * for the enclosing class instance ... the * top one won't always be right (eg. for * local classes); the enclosing one should * be the same as the most enclosing this. */ Row *this = row->scol->this; gboolean res; res = reduce_regenerate_member( expr, &this->expr->root, root ); expr_new_value( expr ); if( !res ) return( FALSE ); } /* We may have made a new class instance ... all our * children need to update their heap pointers. */ if( heapmodel_new_heap( HEAPMODEL( row ), root ) ) return( FALSE ); } } return( TRUE ); } static gboolean row_recomp_row( Row *row ) { Rhs *rhs = row->child_rhs; #ifdef DEBUG printf( "row_recomp_row: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ /* Not much we can do. */ if( !row->expr ) return( TRUE ); /* Clear old error state. */ expr_error_clear( row->expr ); /* Parse and compile any changes to our text since we last came through. */ if( rhs && rhs->itext && heapmodel_update_heap( HEAPMODEL( rhs->itext ) ) ) return( FALSE ); /* We're about to zap the graph: make sure this tree of rows has a * private copy. */ if( !subcolumn_make_private( row->scol ) ) return( FALSE ); /* Regenerate from the expr. */ if( !row_regenerate( row ) ) return( FALSE ); /* Reapply any graphic mods. */ if( rhs && rhs->graphic ) { Classmodel *classmodel = CLASSMODEL( rhs->graphic ); /* If the graphic is non-default, need to set modified to make * sure we reapply the changes. */ if( classmodel->edited ) heapmodel_set_modified( HEAPMODEL( classmodel ), TRUE ); if( heapmodel_update_heap( HEAPMODEL( classmodel ) ) ) return( FALSE ); } progress_update_tick(); return( TRUE ); } static void row_recomp_all( Row *top_row ) { /* Rebuild all dirty rows. */ while( !top_row->err && top_row->recomp ) { Row *dirty_row = ROW( top_row->recomp->data ); #ifdef DEBUG_ROW static GTimer *timer = NULL; if( !timer ) timer = g_timer_new(); g_timer_reset( timer ); #endif /*DEBUG_ROW*/ #ifdef DEBUG_ROW printf( "row_recomp_all: starting " ); row_name_print( dirty_row ); printf( "\n" ); #endif /*DEBUG_ROW*/ if( !row_recomp_row( dirty_row ) ) { /* This will set top_row->err and end the loop. */ if( dirty_row->expr ) expr_error_set( dirty_row->expr ); } else row_dirty_clear( dirty_row ); #ifdef DEBUG_ROW printf( "\t%gs\n", g_timer_elapsed( timer, NULL ) ); #endif /*DEBUG_ROW*/ #ifdef DEBUG printf( "row_recomp_all: after row recomp, top value now " ); pgraph( &top_row->expr->root ); #endif /*DEBUG*/ } } void row_recomp( Row *row ) { Row *top_row = row->top_row; static GTimer *recomp_timer = NULL; if( !recomp_timer ) recomp_timer = g_timer_new(); g_timer_reset( recomp_timer ); /* Sort dirties into recomp order. */ row_recomp_sort( top_row ); /* Take a copy of the recomp list for later testing. */ IM_FREEF( g_slist_free, top_row->recomp_save ); top_row->recomp_save = g_slist_copy( top_row->recomp ); /* Remove all top-level dependencies. */ symbol_link_destroy( top_row->sym ); /* Remove any row recomp links we have. */ (void) row_map_all( top_row, (row_map_fn) row_link_destroy, NULL, NULL, NULL ); /* Rebuild all dirty rows. This may add some dynamic top links. */ row_recomp_all( top_row ); /* Our workspace may have been closed in a callback: bail out. */ if( !top_row->sym ) return; /* Add all static row links. Have to do this after any * parsing in row_recomp_all(). */ (void) row_map_all( top_row, (row_map_fn) row_link_build, NULL, NULL, NULL ); /* Remake all static top-level links. */ (void) symbol_link_build( top_row->sym ); /* Now we know dependencies ... mark everything dirty again. This may * pick up stuff we missed last time and may change the order we * recomp rows in. * * Be careful not to wipe out any errors we found on this first pass. */ slist_map( top_row->recomp_save, (SListMapFn) row_dirty, FALSE ); /* Is this topsym still a leaf? We may have discovered an external * reference to another dirty top-level sym. We can come back here * later. */ if( top_row->sym->ndirtychildren != 0 ) { IM_FREEF( g_slist_free, top_row->recomp_save ); return; } /* Sort dirties into recomp order. */ row_recomp_sort( top_row ); /* Now: if the recomp list is the same as last time, we don't need to * recalc again. */ if( slist_equal( top_row->recomp_save, top_row->recomp ) ) { /* Provided we didn't abandon recomp on an error, we can * just mark all rows clean. */ if( !top_row->err ) slist_map( top_row->recomp, (SListMapFn) row_dirty_clear, NULL ); } else { #ifdef DEBUG_DIRTY printf( "row_recomp: recomp list has changed ... pass 2\n" ); #endif /*DEBUG_DIRTY*/ /* Rebuild all dirty rows in a second pass. */ row_recomp_all( top_row ); /* Our workspace may have been closed in a callback: bail out. */ if( !top_row->sym ) return; } IM_FREEF( g_slist_free, top_row->recomp_save ); /* The symbol can be cleared as well. */ if( !top_row->err ) symbol_dirty_clear( top_row->sym ); /* Now we're clean, all models can update from the heap. Rows * containing errors can have bad pointers in, so careful. */ if( !top_row->err && icontainer_map_all( ICONTAINER( top_row ), (icontainer_map_fn) heapmodel_update_model, NULL ) ) expr_error_set( top_row->expr ); if( main_option_profile ) { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); Symbol *context = symbol_get_parent( top_row->ws->sym ); row_qualified_name_relative( context, top_row, &buf ); printf( "%s\t%g\n", vips_buf_all( &buf ), g_timer_elapsed( recomp_timer, NULL ) ); } #ifdef DEBUG printf( "row_recomp: value of " ); row_name_print( top_row ); printf( "is " ); pgraph( &top_row->expr->root ); #endif /*DEBUG*/ } /* Test, suitable for mapping. */ void * row_is_selected( Row *row ) { if( row->selected ) return( row ); return( NULL ); } /* Deselect a row. */ void * row_deselect( Row *row ) { Workspace *ws = row->ws; if( !row->selected ) return( NULL ); g_assert( ws && IS_WORKSPACE( ws ) ); g_assert( g_slist_find( ws->selected, row ) ); ws->selected = g_slist_remove( ws->selected, row ); row->selected = FALSE; /* Hack: if this is a matrix with selected cells, deselect the matrix * sellection too. We should really have a row method for this I * guess :-( See also workspace_selected_names_sub(). */ if( row->child_rhs && row->child_rhs->graphic && IS_MATRIX( row->child_rhs->graphic ) && MATRIX( row->child_rhs->graphic )->selected ) matrix_deselect( MATRIX( row->child_rhs->graphic ) ); iobject_changed( IOBJECT( row ) ); iobject_changed( IOBJECT( ws ) ); return( NULL ); } /* Select a row. */ static void row_select2( Row *row ) { if( !row->selected ) { Workspace *ws = row->ws; row->selected = TRUE; ws->selected = g_slist_append( ws->selected, row ); iobject_changed( IOBJECT( row ) ); iobject_changed( IOBJECT( ws ) ); } } /* Make sure a row is selected ... used for (eg.) select changed on gktsheet. * No deselection. */ void * row_select_ensure( Row *row ) { row_select2( row ); /* Note for extend select. */ row->top_col->last_select = row; return( NULL ); } /* Select a row, deselecting others first. */ void * row_select( Row *row ) { Workspace *ws = row->ws; workspace_deselect_all( ws ); row_select2( row ); /* Note for extend select. */ row->top_col->last_select = row; return( NULL ); } /* Extend the previous selection. */ void * row_select_extend( Row *row ) { Column *col = row->top_col; Row *last_select = col->last_select; /* Range select if there was a previous selection, and it was in the * same subcolumn. */ if( last_select && row->scol == last_select->scol ) { Subcolumn *scol = row->scol; GSList *rows = ICONTAINER( scol )->children; int pos = g_slist_index( rows, row ); int pos_last = g_slist_index( rows, last_select ); int step = pos > pos_last ? 1 : -1; int i; g_assert( pos != -1 && pos_last != -1 ); for( i = pos_last; i != pos + step; i += step ) row_select2( ROW( g_slist_nth_data( rows, i ) ) ); } else row_select2( row ); /* Note for extend select. */ col->last_select = row; return( NULL ); } /* Toggle a selection. */ void * row_select_toggle( Row *row ) { if( row->selected ) { row_deselect( row ); row->top_col->last_select = NULL; } else { row_select2( row ); row->top_col->last_select = row; } return( NULL ); } /* Do a select action using a modifier. */ void row_select_modifier( Row *row, guint state ) { if( state & GDK_CONTROL_MASK ) row_select_toggle( row ); else if( state & GDK_SHIFT_MASK ) row_select_extend( row ); else row_select( row ); } static void row_set_show( Row *row, RowShowState show ) { if( row->show != show ) { row->show = show; iobject_changed( IOBJECT( row ) ); } } static void * row_show_parent( Link *link, RowShowState show ) { if( link->parent->expr && link->parent->expr->row ) row_set_show( link->parent->expr->row, show ); return( NULL ); } static void * row_show_child( Link *link, RowShowState show ) { if( link->child->expr && link->child->expr->row ) row_set_show( link->child->expr->row, show ); return( NULL ); } void row_show_dependents( Row *row ) { Symbol *topsym = row->top_row->sym; #ifdef DEBUG printf( "row_show_dependents: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ if( topsym ) { slist_map( topsym->topparents, (SListMapFn) row_show_parent, GUINT_TO_POINTER( ROW_SHOW_PARENT ) ); slist_map( topsym->topchildren, (SListMapFn) row_show_child, GUINT_TO_POINTER( ROW_SHOW_CHILD ) ); } } void row_hide_dependents( Row *row ) { Symbol *topsym; #ifdef DEBUG printf( "row_hide_dependents: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ if( row->top_row && (topsym = row->top_row->sym) ) { slist_map( topsym->topparents, (SListMapFn) row_show_parent, GUINT_TO_POINTER( ROW_SHOW_NONE ) ); slist_map( topsym->topchildren, (SListMapFn) row_show_child, GUINT_TO_POINTER( ROW_SHOW_NONE ) ); } } /* Set help for a row. Used by rowview and itextview etc. on mouseover. */ void row_set_status( Row *row ) { Expr *expr = row->expr; char txt[MAX_LINELENGTH]; VipsBuf buf = VIPS_BUF_STATIC( txt ); /* No symbol? eg. on load error. */ if( !expr ) return; row_qualified_name( row, &buf ); if( expr->err ) { vips_buf_appends( &buf, ": " ); vips_buf_appends( &buf, expr->error_top ); } else if( row->child_rhs->itext ) { iText *itext = ITEXT( row->child_rhs->itext ); vips_buf_appends( &buf, " = " ); if( row->ws && row->ws->mode != WORKSPACE_MODE_FORMULA ) vips_buf_appends( &buf, NN( itext->formula ) ); else vips_buf_appends( &buf, vips_buf_all( &itext->value ) ); } workspace_set_status( row->ws, "%s", vips_buf_firstline( &buf ) ); } /* Sub fn of below ... search inside a row hierarcy. Context is (eg.) row * "A1", path is (eg.) "super.name". */ static Row * row_parse_name_row( Row *context, const char *path ) { char name[256]; char *tail; Row *row; Subcolumn *scol; #ifdef DEBUG printf( "row_parse_name_row: \"%s\"\n", path ); #endif /*DEBUG*/ /* Break the name into "thing.tail", where tail could contain other * "." qualifiers. */ im_strncpy( name, path, 256 ); if( !(tail = break_token( name, "." )) ) /* Passed empty string. */ return( context ); /* Needs to be a subcolumn to look inside. We could search the value, * but it's safer to look inside the model we've built from the value. */ if( !context->child_rhs || !context->child_rhs->scol || !(scol = SUBCOLUMN( context->child_rhs->scol )) ) return( NULL ); if( !(row = subcolumn_map( scol, (row_map_fn) iobject_test_name, name, NULL )) ) return( NULL ); return( row_parse_name_row( row, tail ) ); } /* Parse a qualified name .. eg. "untitled.A1.name" and find the row. Find * relative to context. Context is a sym, so we can have workspaceroot etc. */ Row * row_parse_name( Symbol *context, const char *path ) { char name[256]; char *tail; Symbol *sym; Row *row; #ifdef DEBUG printf( "row_parse_name: \"%s\"\n", path ); #endif /*DEBUG*/ /* Break the name into "thing.tail", where tail could contain other * "." qualifiers. */ im_strncpy( name, path, 256 ); if( !(tail = break_token( name, "." )) ) { /* Run out of names ... return this row, if we've found one. */ if( context->expr ) return( context->expr->row ); else return( NULL ); } /* Try to look up name in context. For scopes, we can do it * statically. For other syms, look up in the value of the symbol. */ switch( context->type ) { case SYM_WORKSPACE: case SYM_WORKSPACEROOT: case SYM_ROOT: if( !(sym = compile_lookup( context->expr->compile, name )) ) return( NULL ); break; case SYM_VALUE: if( !(row = context->expr->row) ) return( NULL ); /* Hand off to the row searcher. */ return( row_parse_name_row( row, path ) ); case SYM_ZOMBIE: case SYM_PARAM: case SYM_EXTERNAL: case SYM_BUILTIN: default: /* How odd. */ return( NULL ); } return( row_parse_name( sym, tail ) ); } ================================================ FILE: src/row.h ================================================ /* a row in a workspace ... part of a subcolumn */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_ROW (row_get_type()) #define ROW( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_ROW, Row )) #define ROW_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_ROW, RowClass)) #define IS_ROW( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_ROW )) #define IS_ROW_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_ROW )) #define ROW_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_ROW, RowClass )) /* For when we're flashing the showstate up. */ typedef enum { ROW_SHOW_NONE, ROW_SHOW_PARENT, ROW_SHOW_CHILD } RowShowState; struct _Row { Heapmodel parent_class; /* Our context. */ Subcolumn *scol; /* Enclosing subcolumn */ Rhs *child_rhs; /* Child RHS */ Column *top_col; /* Enclosing top level column */ Workspace *ws; /* Enclosing workspace */ Row *top_row; /* Enclosing root row */ Symbol *sym; /* Symbol we represent */ Expr *expr; /* The expr we edit */ gboolean err; /* Set if this row is on the error list */ gboolean selected; /* Selected or not */ gboolean is_class; /* Display spin buttons */ gboolean popup; /* Set to pop up view on 1st display */ gboolean to_save; /* Should be saved (part of only-save-modded) */ GSList *parents; /* rows which depend on us */ GSList *children; /* rows we depend on */ gboolean dirty; /* If we're marked for recomp */ GSList *recomp; /* If root of class display, subs to recomp */ GSList *recomp_save; /* Previous recomp list */ gboolean depend; /* For spotting dependency loops */ RowShowState show; /* For showing parent/child stuff */ }; typedef struct _RowClass { HeapmodelClass parent_class; /* My methods. */ } RowClass; const char *row_name( Row *row ); void row_qualified_name_relative( Symbol *context, Row *row, VipsBuf *buf ); void row_qualified_name( Row *row, VipsBuf *buf ); void *row_name_print( Row *row ); void row_error_set( Row *row ); void row_error_clear( Row *row ); Workspace *row_get_workspace( Row *row ); GType row_get_type( void ); void row_link_symbol( Row *row, Symbol *sym, PElement *root ); Row *row_new( Subcolumn *scol, Symbol *sym, PElement *root ); void *row_dirty( Row *row, gboolean clear_dirty ); void *row_dirty_intrans( Row *row, gboolean clear_dirty ); void row_recomp( Row *row ); void *row_is_selected( Row *row ); void *row_deselect( Row *row ); void *row_select_ensure( Row *row ); void *row_select( Row *row ); void *row_select_extend( Row *row ); void *row_select_toggle( Row *row ); void row_select_modifier( Row *row, guint state ); void row_show_dependents( Row *row ); void row_hide_dependents( Row *row ); void row_set_status( Row *row ); Row *row_parse_name( Symbol *context, const char *path ); ================================================ FILE: src/rowview.c ================================================ /* A rowview in a workspace ... not a widget, part of column */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ModelClass *parent_class = NULL; enum { ROWVIEW_TARGET_STRING, }; static GtkTargetEntry rowview_target_table[] = { { "STRING", 0, ROWVIEW_TARGET_STRING }, { "text/plain", 0, ROWVIEW_TARGET_STRING } }; /* Just one popup for all tally buttons. */ static GtkWidget *rowview_popup_menu = NULL; static void rowview_destroy( GtkObject *object ) { Rowview *rview; g_return_if_fail( object != NULL ); g_return_if_fail( IS_ROWVIEW( object ) ); rview = ROWVIEW( object ); #ifdef DEBUG printf( "rowview_destroy: " ); row_name_print( ROW( VOBJECT( rview )->iobject ) ); printf( "\n" ); #endif /*DEBUG*/ IM_FREE( rview->last_tooltip ); /* Kill children ... must do this ourselves, since we are not a * self-contained widget. */ DESTROY_GTK( rview->but ); DESTROY_GTK( rview->spin ); DESTROY_GTK( rview->led ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void rowview_attach( Rowview *rview, GtkWidget *child, int x, GtkAttachOptions xoptions, GtkAttachOptions yoptions ) { Subcolumnview *sview = rview->sview; gtk_widget_ref( child ); if( child->parent ) gtk_container_remove( GTK_CONTAINER( sview->table ), child ); gtk_table_attach( GTK_TABLE( sview->table ), child, x, x + 1, rview->rnum, rview->rnum + 1, xoptions, yoptions, 0, 0 ); gtk_widget_unref( child ); } static void rowview_update_widgets( Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); int pos = ICONTAINER( row )->pos; gboolean editable = row->ws->mode != WORKSPACE_MODE_NOEDIT; #ifdef DEBUG printf( "rowview_refresh: " ); row_name_print( row ); printf( "\n" ); printf( "\teditable == %d\n", editable ); #endif /*DEBUG*/ /* Attach widgets to parent in new place. */ if( rview->rnum != pos ) { #ifdef DEBUG printf( "rowview_refresh: move from row %d to row %d\n", rview->rnum, pos ); #endif /*DEBUG*/ rview->rnum = pos; rowview_attach( rview, rview->spin, 0, GTK_FILL, GTK_FILL ); rowview_attach( rview, rview->but, 1, GTK_FILL, GTK_EXPAND | GTK_FILL ); rowview_attach( rview, rview->led, 2, GTK_FILL, GTK_EXPAND | GTK_FILL ); if( rview->rhsview ) rowview_attach( rview, GTK_WIDGET( rview->rhsview ), 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL ); } /* Set colours. */ if( CALC_DISPLAY_LED ) { char *stock_id; stock_id = STOCK_LED_OFF; if( row->selected ) stock_id = STOCK_LED_GREEN; else if( row->show == ROW_SHOW_PARENT ) stock_id = STOCK_LED_CYAN; else if( row->show == ROW_SHOW_CHILD ) stock_id = STOCK_LED_BLUE; else if( row->err ) stock_id = STOCK_LED_RED; else if( row->dirty ) stock_id = STOCK_LED_YELLOW; gtk_image_set_from_stock( GTK_IMAGE( rview->led ), stock_id, GTK_ICON_SIZE_MENU ); } else { gchar *name = ""; if( row->selected ) name = "selected_widget"; else if( row->show == ROW_SHOW_PARENT ) name = "parent_widget"; else if( row->show == ROW_SHOW_CHILD ) name = "child_widget"; else if( row->err ) name = "error_widget"; else if( row->dirty ) name = "dirty_widget"; gtk_widget_set_name( rview->but, name ); } widget_visible( rview->led, rview->visible && CALC_DISPLAY_LED && editable ); /* Update button. */ set_glabel( rview->label, "%s", row_name( row ) ); widget_visible( rview->but, rview->visible && editable ); /* Spin visible only if this is a class. */ widget_visible( rview->spin, rview->visible && row->is_class && editable ); if( rview->rhsview ) widget_visible( GTK_WIDGET( rview->rhsview ), rview->visible ); } static void rowview_reset( View *view ) { Rowview *rview = ROWVIEW( view ); rowview_update_widgets( rview ); VIEW_CLASS( parent_class )->reset( view ); } static void rowview_refresh( vObject *vobject ) { Rowview *rview = ROWVIEW( vobject ); rowview_update_widgets( rview ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } /* Single click on button callback. */ static void rowview_single_cb( GtkWidget *wid, GdkEvent *event, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); row_select_modifier( row, event->button.state ); } /* Edit our object. */ static gboolean rowview_edit( Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); Model *graphic = row->child_rhs->graphic; if( graphic ) model_edit( GTK_WIDGET( rview->sview ), graphic ); return( TRUE ); } /* Double click on button callback. */ static void rowview_double_cb( GtkWidget *button, GdkEvent *event, Rowview *rview ) { if( !rowview_edit( rview ) ) iwindow_alert( button, GTK_MESSAGE_ERROR ); } /* Edit in menu. */ static void rowview_edit_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { if( !rowview_edit( rview ) ) iwindow_alert( button, GTK_MESSAGE_ERROR ); } /* Show info. */ static gboolean rowview_header( Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); Model *graphic = row->child_rhs->graphic; if( graphic ) model_header( GTK_WIDGET( rview->sview ), graphic ); return( TRUE ); } /* Info in menu. */ static void rowview_header_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { if( !rowview_header( rview ) ) iwindow_alert( button, GTK_MESSAGE_ERROR ); } /* Clone the current item. */ static void rowview_clone_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); Workspace *ws = row->top_col->ws; /* Only allow clone of top level rows. */ if( row->top_row != row ) { error_top( _( "Can't duplicate." ) ); error_sub( "%s", _( "You can only duplicate top level rows." ) ); iwindow_alert( button, GTK_MESSAGE_INFO ); return; } workspace_deselect_all( ws ); row_select( row ); if( !workspace_selected_duplicate( ws ) ) iwindow_alert( button, GTK_MESSAGE_ERROR ); workspace_deselect_all( ws ); symbol_recalculate_all(); } /* Ungroup the current item. */ static void rowview_ungroup_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); workspace_deselect_all( row->ws ); row_select( row ); if( !workspace_selected_ungroup( row->ws ) ) iwindow_alert( button, GTK_MESSAGE_ERROR ); symbol_recalculate_all(); } /* Save the current item. */ static void rowview_save_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( rview ) ) ); Row *row = ROW( VOBJECT( rview )->iobject ); Model *graphic = row->child_rhs->graphic; if( graphic ) classmodel_graphic_save( CLASSMODEL( graphic ), GTK_WIDGET( iwnd ) ); } /* Replace the current item. */ static void rowview_replace_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( rview ) ) ); Row *row = ROW( VOBJECT( rview )->iobject ); Model *graphic = row->child_rhs->graphic; if( graphic ) classmodel_graphic_replace( CLASSMODEL( graphic ), GTK_WIDGET( iwnd ) ); } /* Recalculate the current item. */ static void rowview_recalc_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); Workspace *ws = row->top_col->ws; /* Mark dirty from this sym on, and force a recalc even if recalc is * off. */ workspace_deselect_all( ws ); row_select( row ); if( !workspace_selected_recalc( ws ) ) iwindow_alert( button, GTK_MESSAGE_ERROR ); workspace_deselect_all( ws ); /* Now ... pick up any errors. */ if( row->sym && !symbol_recalculate_check( row->sym ) ) iwindow_alert( button, GTK_MESSAGE_ERROR ); } /* Reset the current item. */ static void rowview_clear_edited_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); (void) icontainer_map_all( ICONTAINER( row ), (icontainer_map_fn) model_clear_edited, NULL ); symbol_recalculate_all(); } /* Remove the current item. */ static void rowview_remove_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); Workspace *ws = row->top_col->ws; workspace_deselect_all( ws ); row_select( row ); workspace_selected_remove_yesno( ws, button ); } /* Callback for up/down spin button. */ static void rowview_spin_up_cb( GtkWidget *widget, gpointer client ) { Rowview *rview = ROWVIEW( client ); Row *row = ROW( VOBJECT( rview )->iobject ); Rhs *rhs = row->child_rhs; rhs_vislevel_down( rhs ); workspace_set_modified( row->ws, TRUE ); } static void rowview_spin_down_cb( GtkWidget *widget, gpointer client ) { Rowview *rview = ROWVIEW( client ); Row *row = ROW( VOBJECT( rview )->iobject ); Rhs *rhs = row->child_rhs; rhs_vislevel_up( rhs ); workspace_set_modified( row->ws, TRUE ); } /* Scroll to make tally entry visible. */ static void rowview_scrollto( View *view, ModelScrollPosition position ) { Rowview *rview = ROWVIEW( view ); Columnview *cview = view_get_columnview( VIEW( rview ) ); Workspaceview *wview = cview->wview; int x, y, w, h; /* Extract position of tally row in RC widget. */ rowview_get_position( rview, &x, &y, &w, &h ); workspaceview_scroll( wview, x, y, w, h ); } static void rowview_drag( Rowview *rview_from, Rowview *rview_to ) { Row *row_from = ROW( VOBJECT( rview_from )->iobject ); Row *row_to = ROW( VOBJECT( rview_to )->iobject ); if( row_from->top_col != row_to->top_col ) { error_top( _( "Not implemented." ) ); error_sub( _( "Drag between columns not yet implemented." ) ); iwindow_alert( GTK_WIDGET( rview_from ), GTK_MESSAGE_ERROR ); return; } icontainer_child_move( ICONTAINER( row_from ), ICONTAINER( row_to )->pos ); /* Refresh all the rows, to make sure we move all rows to their new * slots. */ icontainer_map( ICONTAINER( row_from->scol ), (icontainer_map_fn) iobject_changed, NULL, NULL ); workspace_deselect_all( row_from->ws ); } static void rowview_drag_data_get( GtkWidget *but, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time, Rowview *rview ) { if( info == ROWVIEW_TARGET_STRING ) { /* Send a pointer to us. */ gtk_selection_data_set( selection_data, selection_data->target, 8, (const guchar *) &rview, sizeof( Rowview * ) ); } } static void rowview_drag_data_received( GtkWidget *but, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, Rowview *rview_to ) { if( data->length == sizeof( Rowview * ) && data->format == 8 && info == ROWVIEW_TARGET_STRING ) { Rowview *rview_from = *((Rowview **) data->data); if( IS_ROWVIEW( rview_from ) ) { rowview_drag( rview_from, rview_to ); gtk_drag_finish( context, TRUE, FALSE, time ); return; } } gtk_drag_finish( context, FALSE, FALSE, time ); } /* Attach the rowview menu to a widget ... also used by iimageview */ guint rowview_menu_attach( Rowview *rview, GtkWidget *widget ) { return( popup_attach( widget, rowview_popup_menu, rview ) ); } static void rowview_link( View *view, Model *model, View *parent ) { Row *row = ROW( model ); Rowview *rview = ROWVIEW( view ); Subcolumnview *sview = SUBCOLUMNVIEW( parent ); VIEW_CLASS( parent_class )->link( view, model, parent ); rview->sview = sview; /* Only drag n drop top level rows. */ if( row->top_row == row ) { gtk_drag_source_set( rview->but, GDK_BUTTON1_MASK, rowview_target_table, IM_NUMBER( rowview_target_table ), GDK_ACTION_COPY ); gtk_signal_connect( GTK_OBJECT( rview->but ), "drag_data_get", GTK_SIGNAL_FUNC( rowview_drag_data_get ), rview ); gtk_drag_dest_set( rview->but, GTK_DEST_DEFAULT_ALL, rowview_target_table, IM_NUMBER( rowview_target_table ), GDK_ACTION_COPY ); gtk_signal_connect( GTK_OBJECT( rview->but ), "drag_data_received", GTK_SIGNAL_FUNC( rowview_drag_data_received ), rview ); } rowview_menu_attach( rview, rview->but ); } static void rowview_child_add( View *parent, View *child ) { Rowview *rowview = ROWVIEW( parent ); g_assert( IS_RHSVIEW( child ) ); g_assert( !rowview->rhsview ); rowview->rhsview = RHSVIEW( child ); VIEW_CLASS( parent_class )->child_add( parent, child ); } static void rowview_child_remove( View *parent, View *child ) { Rowview *rowview = ROWVIEW( parent ); g_assert( IS_RHSVIEW( child ) ); g_assert( rowview->rhsview ); rowview->rhsview = NULL; VIEW_CLASS( parent_class )->child_remove( parent, child ); } static void rowview_class_init( RowviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; GtkWidget *pane; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ object_class->destroy = rowview_destroy; vobject_class->refresh = rowview_refresh; view_class->link = rowview_link; view_class->child_add = rowview_child_add; view_class->child_remove = rowview_child_remove; view_class->reset = rowview_reset; view_class->scrollto = rowview_scrollto; /* Other init. */ pane = rowview_popup_menu = popup_build( _( "Row menu" ) ); popup_add_but( pane, _( "_Edit" ), POPUP_FUNC( rowview_edit_cb ) ); popup_add_but( pane, _( "_Header" ), POPUP_FUNC( rowview_header_cb ) ); popup_add_but( pane, STOCK_DUPLICATE, POPUP_FUNC( rowview_clone_cb ) ); popup_add_but( pane, _( "U_ngroup" ), POPUP_FUNC( rowview_ungroup_cb ) ); popup_add_but( pane, GTK_STOCK_SAVE_AS, POPUP_FUNC( rowview_save_cb ) ); popup_add_but( pane, _( "Replace From _File" ), POPUP_FUNC( rowview_replace_cb ) ); popup_add_but( pane, _( "_Recalculate" ), POPUP_FUNC( rowview_recalc_cb ) ); popup_add_but( pane, _( "Re_set" ), POPUP_FUNC( rowview_clear_edited_cb ) ); menu_add_sep( pane ); popup_add_but( pane, GTK_STOCK_DELETE, POPUP_FUNC( rowview_remove_cb ) ); } static void rowview_enter_cb( GtkWidget *widget, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); row_set_status( row ); row_show_dependents( row ); } static void rowview_leave_cb( GtkWidget *widget, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); row_hide_dependents( row ); } static gboolean rowview_focus_cb( GtkWidget *widget, GtkDirectionType dir, Rowview *rview ) { view_scrollto( VIEW( rview ), MODEL_SCROLL_TOP ); return( FALSE ); } static void rowview_tooltip_generate( GtkWidget *widget, VipsBuf *buf, Rowview *rview ) { Row *row = ROW( VOBJECT( rview )->iobject ); iobject_info( IOBJECT( row ), buf ); vips_buf_removec( buf, '\n' ); } static void rowview_init( Rowview *rview ) { rview->visible = TRUE; rview->rnum = -1; rview->last_tooltip = NULL; /* Make leds. */ rview->led = gtk_image_new_from_stock( STOCK_LED_OFF, GTK_ICON_SIZE_MENU ); gtk_misc_set_alignment( GTK_MISC( rview->led ), 0.5, 0.0 ); gtk_misc_set_padding( GTK_MISC( rview->led ), 2, 2 ); /* Make fold/unfold button. */ rview->spin = spin_new(); gtk_signal_connect( GTK_OBJECT( rview->spin ), "up_click", GTK_SIGNAL_FUNC( rowview_spin_up_cb ), rview ); gtk_signal_connect( GTK_OBJECT( rview->spin ), "down_click", GTK_SIGNAL_FUNC( rowview_spin_down_cb ), rview ); gtk_widget_show( rview->spin ); set_tooltip( rview->spin, _( "Click to open or close class" ) ); /* Make name button. */ rview->but = gtk_button_new(); gtk_widget_show( rview->but ); doubleclick_add( rview->but, FALSE, DOUBLECLICK_FUNC( rowview_single_cb ), rview, DOUBLECLICK_FUNC( rowview_double_cb ), rview ); rview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( rview->label ), 1, 0 ); gtk_misc_set_padding( GTK_MISC( rview->label ), 2, 0 ); gtk_container_add( GTK_CONTAINER( rview->but ), rview->label ); gtk_widget_show( rview->label ); gtk_signal_connect( GTK_OBJECT( rview->but ), "enter", GTK_SIGNAL_FUNC( rowview_enter_cb ), rview ); gtk_signal_connect( GTK_OBJECT( rview->but ), "leave", GTK_SIGNAL_FUNC( rowview_leave_cb ), rview ); gtk_signal_connect( GTK_OBJECT( rview->but ), "focus", GTK_SIGNAL_FUNC( rowview_focus_cb ), rview ); set_tooltip_generate( rview->but, (TooltipGenerateFn) rowview_tooltip_generate, rview, NULL ); } GtkType rowview_get_type( void ) { static GtkType rowview_type = 0; if( !rowview_type ) { static const GtkTypeInfo rview_info = { "Rowview", sizeof( Rowview ), sizeof( RowviewClass ), (GtkClassInitFunc) rowview_class_init, (GtkObjectInitFunc) rowview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; rowview_type = gtk_type_unique( TYPE_VIEW, &rview_info ); } return( rowview_type ); } View * rowview_new( void ) { Rowview *rview = gtk_type_new( TYPE_ROWVIEW ); return( VIEW( rview ) ); } /* Find the position and size of a row in the enclosing GtkFixed. */ void rowview_get_position( Rowview *rview, int *x, int *y, int *w, int *h ) { Columnview *cview = view_get_columnview( VIEW( rview ) ); if( GTK_WIDGET_VISIBLE( rview->spin ) ) { *x = rview->spin->allocation.x; *y = rview->spin->allocation.y; *w = rview->spin->allocation.width; *h = rview->spin->allocation.height; } else { *x = rview->but->allocation.x; *y = rview->but->allocation.y; *w = 0; *h = 0; } *w += rview->but->allocation.width; *h = IM_MAX( rview->but->allocation.height, *h ); if( GTK_WIDGET_VISIBLE( rview->led ) ) { *w += rview->led->allocation.width; *h = IM_MAX( rview->led->allocation.height, *h ); } *w += GTK_WIDGET( rview->rhsview )->allocation.width; *h = IM_MAX( GTK_WIDGET( rview->rhsview )->allocation.height, *h ); /* Title bar, plus separator. */ *y += cview->title->allocation.height + 2; *x += cview->main->allocation.x; *y += cview->main->allocation.y; #ifdef DEBUG printf( "rowview_get_position: " ); row_name_print( ROW( VOBJECT( rview )->iobject ) ); printf( ": x = %d, y = %d, w = %d, h = %d\n", *x, *y, *w, *h ); #endif /*DEBUG*/ } /* Hide/show a row. */ void rowview_set_visible( Rowview *rview, gboolean visible ) { if( rview->visible != visible ) { rview->visible = visible; rowview_update_widgets( rview ); } } gboolean rowview_get_visible( Rowview *rview ) { return( rview->visible ); } ================================================ FILE: src/rowview.h ================================================ /* a rowview in a workspace ... part of a tallycolumn, not a separate widget */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_ROWVIEW (rowview_get_type()) #define ROWVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_ROWVIEW, Rowview )) #define ROWVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_ROWVIEW, RowviewClass )) #define IS_ROWVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_ROWVIEW )) #define IS_ROWVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_ROWVIEW )) struct _Rowview { View view; Subcolumnview *sview; /* Enclosing subcolumnview */ Rhsview *rhsview; /* Our rhs */ gboolean visible; /* Currently visible */ int rnum; /* Row of tallycolumn we are in */ GtkWidget *spin; /* Class display open/close widgets */ GtkWidget *but; /* Name button */ GtkWidget *led; /* Indicators */ GtkWidget *label; /* Name label */ char *last_tooltip; /* Last tooltip we set */ }; typedef struct _RowviewClass { ViewClass parent_class; /* My methods. */ } RowviewClass; guint rowview_menu_attach( Rowview *rview, GtkWidget *widget ); GtkType rowview_get_type( void ); View *rowview_new( void ); void rowview_get_position( Rowview *rview, int *x, int *y, int *w, int *h ); void rowview_set_visible( Rowview *rview, gboolean visible ); gboolean rowview_get_visible( Rowview *rview ); ================================================ FILE: src/secret.c ================================================ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Just show secrets we added #define DEBUG_ADD */ #include "ip.h" /* build secret sets for exprs cases: fred a = jim 12 { jim b = a + b; } jim refers to a parameter in an enclosing scope ... we add extra secret parameters to jim like this: fred a = jim [a] 12 { jim [a] b = a + b; } across class boundaries: fred a = jim { jim = class { b = a; } } now fred.jim.b refers to fred.a ... a needs to be added to the secrets on jim's constructor like this: fred a = jim [a] { jim [a] = class { b = a; } } if the secret is a class member, pass "this" instead and the inner thing then gets from that fred a = class { jim [fred.this] b = fred.this.a + b; } if the inner thing is also a class, need to get in two stages ... first get the right this, then get from that fred a = class { jim [fred.this] = class { b = jim.this.fred.this.a; } } need to work for any sort of nesting of functions and classes fred = class { b = c { c = this; } } not just params ... can involve locals of parents */ /* Add a secret. Set changed if we make a change. */ static void * secret_add( Symbol *secret, Compile *compile, gboolean *changed ) { Compile *parent = compile_get_parent( compile ); #ifdef DEBUG printf( "secret_add: considering secret " ); symbol_name_print( secret ); printf( "for " ); compile_name_print( compile ); printf( " ...\n" ); #endif /*DEBUG*/ /* If expr is a class, don't add our own this. */ if( is_class( compile ) && secret == compile->this ) return( NULL ); /* If expr already has secret as a param or secret, don't add again. */ if( g_slist_find( compile->secret, secret ) || g_slist_find( compile->param, secret ) ) return( NULL ); /* If secret is a member (param, func, whatever), add secret's * enclosing "this" instead ... expr can then get secret from there. * Unless the secret is already a "this", of course. */ if( is_class( COMPILE( ICONTAINER( secret )->parent ) ) && !is_this( secret ) ) secret = COMPILE( ICONTAINER( secret )->parent )->this; /* If compile is a member (and not a class itself), add the secret to * compile's constructor instead ... compile can get from "this". */ if( is_class( parent ) && secret != parent->this && !is_class( compile ) ) compile = parent; /* We may have moved stuff about ... check for dupes again. */ if( g_slist_find( compile->secret, secret ) || g_slist_find( compile->param, secret ) ) return( NULL ); #ifdef DEBUG_ADD printf( "secret_add: adding secret " ); symbol_name_print( secret ); printf( "to " ); compile_name_print( compile ); printf( "\n" ); #endif /*DEBUG_ADD*/ compile->secret = g_slist_append( compile->secret, secret ); compile->nsecret += 1; *changed = TRUE; return( NULL ); } /* If compile is a member, then secret lists are easy ... just use "this". */ static void * secret_set_class( Compile *compile ) { if( is_class( compile_get_parent( compile ) ) ) { Compile *parent = compile_get_parent( compile ); Symbol *ths = parent->this; gboolean changed; if( secret_add( ths, compile, &changed ) ) return( (void *) ths ); } return( NULL ); } /* child is one of compile's children ... is it reference to a parameter * in an enclosing scope? If yes, we've found a secret! */ static void * secret_is_nonlocal( Symbol *child, Compile *compile ) { gboolean changed; if( child->type == SYM_PARAM && COMPILE( ICONTAINER( child )->parent ) != compile ) { if( secret_add( child, compile, &changed ) ) return( child ); } return( NULL ); } /* Make initial secret list ... if this is a member/function, search for * references to symbols in an enclosing scope. */ static void * secret_find_nonlocal( Compile *compile ) { /* Look for any secrets. */ if( slist_map( compile->children, (SListMapFn) secret_is_nonlocal, compile ) ) return( compile ); return( NULL ); } /* Does child have any secrets that compile does not? */ static void * secret_test( Symbol *child, Compile *compile, gboolean *changed ) { /* If this is a parameter or a zombie, nothing to do. */ if( !is_value( child ) ) return( NULL ); if( child->expr->compile ) if( slist_map2( child->expr->compile->secret, (SListMap2Fn) secret_add, compile, changed ) ) return( child ); return( NULL ); } /* Close secret list ... if sym has a child with a secret sym does not have, * sym needs child's secret too. */ static void * secret_close( Compile *compile, gboolean *changed ) { if( is_class( compile ) ) { /* For classes, need to consider all of their locals. Any * secrets our locals have, we need too. */ if( icontainer_map( ICONTAINER( compile ), (icontainer_map_fn) secret_test, compile, changed ) ) return( compile ); } else { /* Look at our immediate children, any of them have secrets * we don't? */ if( slist_map2( compile->children, (SListMap2Fn) secret_test, compile, changed ) ) return( compile ); } return( NULL ); } #ifdef DEBUG /* Sub-fn of below ... add param as a secret to sym. */ static void * secret_all_add( Compile *compile, Symbol *param ) { gboolean changed; return( secret_add( param, compile, &changed ) ); } /* Sub-fn of below ... add param as a secret for all of compile's locals. */ static void * secret_all_sym( Symbol *param, Compile *compile ) { return( compile_map_all( compile, (map_compile_fn) secret_all_add, param ) ); } /* Make syms params and secrets secrets for all sub-syms. Only handy for * debugging. */ static void * secret_all( Compile *compile ) { if( slist_map( compile->param, (SListMapFn) secret_all_sym, compile ) || slist_map( compile->secret, (SListMapFn) secret_all_sym, compile ) ) return( compile ); return( NULL ); } #endif /*DEBUG*/ /* Make secret param lists for compile and all of it's sub-defs. */ void secret_build( Compile *compile ) { gboolean changed; #ifdef DEBUG_ADD printf( "secret_build: " ); symbol_name_print( compile->sym ); printf( "\n" ); #endif /*DEBUG_ADD*/ /* Look for class definitions ... all members of a * class should take a single secret, their "this" parameter. * When they in turn call their locals, they can get the * secrets they need from "this". */ (void) compile_map_all( compile, (map_compile_fn) secret_set_class, NULL ); /* Now look for non-member functions ... if they reference * parameters in an enclosing scope, add that parameter to * the secret list. */ (void) compile_map_all( compile, (map_compile_fn) secret_find_nonlocal, NULL ); /* Now take the closure of the secret lists ... have to * fix() this to get limit of secret_close(). */ do { changed = FALSE; (void) compile_map_all( compile, (map_compile_fn) secret_close, &changed ); } while( changed ); } ================================================ FILE: src/secret.h ================================================ /* Decls for secret.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ void secret_build( Compile *compile ); ================================================ FILE: src/slider.c ================================================ /* an input slider ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static View * slider_view_new( Model *model, View *parent ) { return( sliderview_new() ); } /* Members of slider we automate. */ static ClassmodelMember slider_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( iObject, caption ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_FROM, "from", N_( "From" ), G_STRUCT_OFFSET( Slider, from ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_TO, "to", N_( "To" ), G_STRUCT_OFFSET( Slider, to ) }, { CLASSMODEL_MEMBER_DOUBLE, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Slider, value ) } }; static void slider_class_init( SliderClass *class ) { ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ model_class->view_new = slider_view_new; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = slider_members; classmodel_class->n_members = IM_NUMBER( slider_members ); } static void slider_init( Slider *slider ) { /* Overridden later. Just something sensible. */ slider->from = 0; slider->to = 255; slider->value = 128; /* Need to set caption to something too, since it's an automated * member. */ iobject_set( IOBJECT( slider ), CLASS_SLIDER, "" ); } GType slider_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( SliderClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) slider_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Slider ), 32, /* n_preallocs */ (GInstanceInitFunc) slider_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Slider", &info, 0 ); } return( type ); } ================================================ FILE: src/slider.h ================================================ /* a slider in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_SLIDER (slider_get_type()) #define SLIDER( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_SLIDER, Slider )) #define SLIDER_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_SLIDER, SliderClass)) #define IS_SLIDER( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_SLIDER )) #define IS_SLIDER_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_SLIDER )) #define SLIDER_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_SLIDER, SliderClass )) typedef struct _Slider { Classmodel parent_object; double from, to, value; } Slider; typedef struct _SliderClass { ClassmodelClass parent_class; /* My methods. */ } SliderClass; GType slider_get_type( void ); ================================================ FILE: src/sliderview.c ================================================ /* run the display for a slider in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; static void sliderview_refresh( vObject *vobject ) { Sliderview *sliderview = SLIDERVIEW( vobject ); Slider *slider = SLIDER( VOBJECT( sliderview )->iobject ); Tslider *tslider = sliderview->tslider; const double range = slider->to - slider->from; const double lrange = log10( range ); const char *caption = IOBJECT( slider )->caption; #ifdef DEBUG printf( "sliderview_refresh\n" ); #endif /*DEBUG*/ /* Compatibility ... we used to not have a caption. Don't display * anything if there's o caption. */ if( caption ) { if( strcmp( caption, "" ) != 0 ) set_glabel( sliderview->label, _( "%s:" ), caption ); else set_glabel( sliderview->label, "%s", "" ); } tslider->from = slider->from; tslider->to = slider->to; tslider->svalue = slider->value; tslider->value = slider->value; tslider->digits = IM_MAX( 0, ceil( 2 - lrange ) ); if( CALC_RECOMP_SLIDER ) gtk_range_set_update_policy( GTK_RANGE( tslider->slider ), GTK_UPDATE_CONTINUOUS ); else gtk_range_set_update_policy( GTK_RANGE( tslider->slider ), GTK_UPDATE_DISCONTINUOUS ); #ifdef DEBUG gtk_range_set_update_policy( GTK_RANGE( tslider->slider ), GTK_UPDATE_DISCONTINUOUS ); #endif /*DEBUG*/ tslider_changed( tslider ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void * sliderview_scan( View *view ) { Sliderview *sliderview = SLIDERVIEW( view ); Slider *slider = SLIDER( VOBJECT( sliderview )->iobject ); Classmodel *classmodel = CLASSMODEL( slider ); Expr *expr = HEAPMODEL( classmodel )->row->expr; double value; if( !get_geditable_double( sliderview->tslider->entry, &value ) ) { expr_error_set( expr ); return( view ); } if( slider->value != value ) { slider->value = value; classmodel_update( classmodel ); } return( VIEW_CLASS( parent_class )->scan( view ) ); } static void sliderview_link( View *view, Model *model, View *parent ) { Sliderview *sliderview = SLIDERVIEW( view ); VIEW_CLASS( parent_class )->link( view, model, parent ); if( GRAPHICVIEW( view )->sview ) gtk_size_group_add_widget( GRAPHICVIEW( view )->sview->group, sliderview->label ); } static void sliderview_class_init( SliderviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = sliderview_refresh; view_class->scan = sliderview_scan; view_class->link = sliderview_link; } /* Drag on slider. */ static void sliderview_change_cb( Tslider *tslider, Sliderview *sliderview ) { Slider *slider = SLIDER( VOBJECT( sliderview )->iobject ); #ifdef DEBUG printf( "sliderview_change_cb\n" ); #endif /*DEBUG*/ if( slider->value != tslider->svalue ) { slider->value = tslider->svalue; classmodel_update( CLASSMODEL( slider ) ); symbol_recalculate_all(); } } static void sliderview_init( Sliderview *sliderview ) { GtkWidget *hbox; hbox = gtk_hbox_new( FALSE, 12 ); gtk_box_pack_start( GTK_BOX( sliderview ), hbox, TRUE, FALSE, 0 ); sliderview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( sliderview->label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( sliderview->label ), 2, 1 ); gtk_box_pack_start( GTK_BOX( hbox ), sliderview->label, FALSE, FALSE, 0 ); sliderview->tslider = tslider_new(); tslider_set_conversions( sliderview->tslider, NULL, NULL ); gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( sliderview->tslider ), TRUE, TRUE, 6 ); gtk_signal_connect_object( GTK_OBJECT( sliderview->tslider ), "text_changed", GTK_SIGNAL_FUNC( view_changed_cb ), GTK_OBJECT( sliderview ) ); gtk_signal_connect_object( GTK_OBJECT( sliderview->tslider ), "activate", GTK_SIGNAL_FUNC( view_activate_cb ), GTK_OBJECT( sliderview ) ); gtk_signal_connect( GTK_OBJECT( sliderview->tslider ), "slider_changed", GTK_SIGNAL_FUNC( sliderview_change_cb ), sliderview ); gtk_widget_show_all( GTK_WIDGET( sliderview ) ); } GtkType sliderview_get_type( void ) { static GtkType sliderview_type = 0; if( !sliderview_type ) { static const GtkTypeInfo sinfo = { "Sliderview", sizeof( Sliderview ), sizeof( SliderviewClass ), (GtkClassInitFunc) sliderview_class_init, (GtkObjectInitFunc) sliderview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; sliderview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &sinfo ); } return( sliderview_type ); } View * sliderview_new( void ) { Sliderview *sliderview = gtk_type_new( TYPE_SLIDERVIEW ); return( VIEW( sliderview ) ); } ================================================ FILE: src/sliderview.h ================================================ /* a sliderview in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_SLIDERVIEW (sliderview_get_type()) #define SLIDERVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_SLIDERVIEW, Sliderview )) #define SLIDERVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_SLIDERVIEW, SliderviewClass )) #define IS_SLIDERVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_SLIDERVIEW )) #define IS_SLIDERVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_SLIDERVIEW )) typedef struct _Sliderview { Graphicview parent_object; /* My instance vars. */ GtkWidget *label; Tslider *tslider; } Sliderview; typedef struct _SliderviewClass { GraphicviewClass parent_class; /* My methods. */ } SliderviewClass; GtkType sliderview_get_type( void ); View *sliderview_new( void ); ================================================ FILE: src/spin.c ================================================ /* a pair of spin buttons, with no entry ... don't actually use buttons, * since we may have lots and lots of these, and we don't want to make an X * window for each one * * we do the event handling ourselves ... our enclosing view passes the ev * to spin_event(), this triggers signals as required */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; /* Our signals. Up and down click. */ enum { UP_CLICK, DOWN_CLICK, LAST_SIGNAL }; static guint spin_signals[LAST_SIGNAL] = { 0 }; /* Default up and down signal handlers. */ static void spin_real_up_click( Spin *spin ) { #ifdef DEBUG printf( "spin_real_up_click\n" ); #endif /*DEBUG*/ } static void spin_real_down_click( Spin *spin ) { #ifdef DEBUG printf( "spin_real_down_click\n" ); #endif /*DEBUG*/ } static void spin_class_init( SpinClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ spin_signals[UP_CLICK] = g_signal_new( "up_click", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( SpinClass, up_click ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); spin_signals[DOWN_CLICK] = g_signal_new( "down_click", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( SpinClass, down_click ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); class->up_click = spin_real_up_click; class->down_click = spin_real_down_click; } typedef struct { Spin *spin; int x, y; /* Click position */ gboolean handled; } SpinEvent; static void allocation2rect( GtkAllocation *from, Rect *to ) { to->left = from->x; to->top = from->y; to->width = from->width; to->height = from->height; } static void spin_button_press_event_test( GtkWidget *widget, gpointer data ) { SpinEvent *sev = (SpinEvent *) data; Rect pos; if( sev->handled ) return; allocation2rect( &widget->allocation, &pos ); if( im_rect_includespoint( &pos, sev->x, sev->y ) ) { if( GTK_IS_ARROW( widget ) ) { sev->handled = TRUE; if( GTK_ARROW( widget )->arrow_type == GTK_ARROW_UP ) g_signal_emit( GTK_OBJECT( sev->spin ), spin_signals[UP_CLICK], 0 ); else g_signal_emit( GTK_OBJECT( sev->spin ), spin_signals[DOWN_CLICK], 0 ); } } } /* Event in us somewhere. */ static gboolean spin_button_press_event_cb( GtkWidget *widget, GdkEventButton *event, Spin *spin ) { gboolean handled = FALSE; if( event->type == GDK_BUTTON_PRESS ) { SpinEvent sev; if( event->button == 1 ) { sev.spin = spin; /* Find button x/y relative to top LH corner of spin. */ sev.x = event->x + GTK_WIDGET( spin )->allocation.x; sev.y = event->y + GTK_WIDGET( spin )->allocation.y; sev.handled = FALSE; spin_button_press_event_test( spin->up, &sev ); spin_button_press_event_test( spin->down, &sev ); handled = sev.handled; } } return( handled ); } static gboolean spin_button_enter_notify_event_cb( GtkWidget *widget, GdkEventCrossing *event, Spin *spin ) { gboolean handled = FALSE; if( event->detail != GDK_NOTIFY_INFERIOR ) gtk_widget_set_state( widget, GTK_STATE_PRELIGHT ); return( handled ); } static gboolean spin_button_leave_notify_event_cb( GtkWidget *widget, GdkEventCrossing *event, Spin *spin ) { gboolean handled = FALSE; if( event->detail != GDK_NOTIFY_INFERIOR ) gtk_widget_set_state( widget, GTK_STATE_NORMAL ); return( handled ); } static void spin_init( Spin *spin ) { GtkWidget *ebox; GtkWidget *vbox; ebox = gtk_event_box_new(); set_tooltip( ebox, _( "Expand or collapse row" ) ); gtk_event_box_set_visible_window( GTK_EVENT_BOX( ebox ), FALSE ); gtk_signal_connect( GTK_OBJECT( ebox ), "button_press_event", GTK_SIGNAL_FUNC( spin_button_press_event_cb ), spin ); gtk_signal_connect( GTK_OBJECT( ebox ), "enter_notify_event", GTK_SIGNAL_FUNC( spin_button_enter_notify_event_cb ), spin ); gtk_signal_connect( GTK_OBJECT( ebox ), "leave_notify_event", GTK_SIGNAL_FUNC( spin_button_leave_notify_event_cb ), spin ); gtk_box_pack_start( GTK_BOX( spin ), ebox, FALSE, FALSE, 0 ); gtk_widget_show( ebox ); vbox = gtk_vbox_new( 0, FALSE ); gtk_container_add( GTK_CONTAINER( ebox ), vbox ); gtk_widget_show( vbox ); spin->up = gtk_arrow_new( GTK_ARROW_UP, GTK_SHADOW_OUT ); spin->down = gtk_arrow_new( GTK_ARROW_DOWN, GTK_SHADOW_OUT ); gtk_box_pack_start( GTK_BOX( vbox ), spin->up, FALSE, FALSE, 0 ); gtk_box_pack_end( GTK_BOX( vbox ), spin->down, FALSE, FALSE, 0 ); gtk_widget_show( spin->up ); gtk_widget_show( spin->down ); } GtkType spin_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Spin", sizeof( Spin ), sizeof( SpinClass ), (GtkClassInitFunc) spin_class_init, (GtkObjectInitFunc) spin_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VIEW, &info ); } return( type ); } GtkWidget * spin_new( void ) { Spin *spin = (Spin *) gtk_type_new( TYPE_SPIN ); return( GTK_WIDGET( spin ) ); } ================================================ FILE: src/spin.h ================================================ /* a pair of spin buttons */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_SPIN (spin_get_type()) #define SPIN( obj ) (GTK_CHECK_CAST( (obj), TYPE_SPIN, Spin )) #define SPIN_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_SPIN, SpinClass )) #define IS_SPIN( obj ) (GTK_CHECK_TYPE( (obj), TYPE_SPIN )) #define IS_SPIN_CLASS( klass ) (GTK_CHECK_CLASS_TYPE( (klass), TYPE_SPIN )) typedef struct _Spin { View view; /* My instance vars. */ GtkWidget *up; /* Arrow buttons */ GtkWidget *down; } Spin; typedef struct _SpinClass { ViewClass parent_class; void (*up_click)( Spin * ); void (*down_click)( Spin * ); } SpinClass; GtkType spin_get_type( void ); GtkWidget *spin_new( void ); ================================================ FILE: src/statusview.c ================================================ /* widgets for the status bar */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GtkFrameClass *parent_class = NULL; /* The popup menu. */ static GtkWidget *statusview_menu = NULL; /* Sub. fn. of below. Junk the widgets inside a band display. */ static void * statusviewband_destroy_sub( StatusviewBand *svb ) { DESTROY_GTK( svb->val ); IM_FREE( svb ); return( NULL ); } /* Junk the widgets inside a band display. */ static void statusviewband_destroy( Statusview *sv ) { slist_map( sv->bands, (SListMapFn) statusviewband_destroy_sub, NULL ); IM_FREEF( g_slist_free, sv->bands ); } static void statusview_destroy( GtkObject *object ) { Statusview *sv; g_return_if_fail( object != NULL ); g_return_if_fail( IS_STATUSVIEW( object ) ); sv = STATUSVIEW( object ); #ifdef DEBUG printf( "statusview_destroy\n" ); #endif /*DEBUG*/ statusviewband_destroy( sv ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* Hide this statusview. */ static void statusview_hide_cb( GtkWidget *menu, GtkWidget *host, Statusview *sv ) { sv->imagemodel->show_status = FALSE; iobject_changed( IOBJECT( sv->imagemodel ) ); } static void statusview_class_init( StatusviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidget *pane; parent_class = g_type_class_peek_parent( class ); object_class->destroy = statusview_destroy; /* Create signals. */ /* Init methods. */ pane = statusview_menu = popup_build( _( "Status bar menu" ) ); popup_add_but( pane, GTK_STOCK_CLOSE, POPUP_FUNC( statusview_hide_cb ) ); } static void statusview_init( Statusview *sv ) { GtkWidget *vb, *hb; GtkWidget *eb; sv->imagemodel = NULL; sv->bands = NULL; sv->fmt = -1; sv->nb = -1; gtk_frame_set_shadow_type( GTK_FRAME( sv ), GTK_SHADOW_OUT ); eb = gtk_event_box_new(); gtk_container_add( GTK_CONTAINER( sv ), eb ); popup_attach( eb, statusview_menu, sv ); vb = gtk_vbox_new( FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( vb ), 1 ); gtk_container_add( GTK_CONTAINER( eb ), vb ); sv->top = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( sv->top ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( vb ), sv->top, TRUE, TRUE, 0 ); hb = gtk_hbox_new( FALSE, 5 ); gtk_box_pack_start( GTK_BOX( vb ), hb, TRUE, TRUE, 0 ); sv->pos = gtk_label_new( "" ); set_fixed( sv->pos, strlen( "(888888,888888)" ) ); gtk_misc_set_alignment( GTK_MISC( sv->pos ), 0.0, 0.5 ); gtk_box_pack_start( GTK_BOX( hb ), sv->pos, FALSE, FALSE, 0 ); sv->hb = gtk_hbox_new( FALSE, 5 ); gtk_box_pack_start( GTK_BOX( hb ), sv->hb, TRUE, TRUE, 0 ); sv->mag = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( sv->mag ), 0.0, 0.5 ); gtk_box_pack_end( GTK_BOX( hb ), sv->mag, FALSE, FALSE, 0 ); gtk_widget_show_all( eb ); } GtkType statusview_get_type( void ) { static GtkType statusview_type = 0; if( !statusview_type ) { static const GtkTypeInfo sinfo = { "Statusview", sizeof( Statusview ), sizeof( StatusviewClass ), (GtkClassInitFunc) statusview_class_init, (GtkObjectInitFunc) statusview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; statusview_type = gtk_type_unique( GTK_TYPE_FRAME, &sinfo ); } return( statusview_type ); } /* Our model has changed ... update. */ static void statusview_changed_cb( Imagemodel *imagemodel, Statusview *sv ) { static char *sample[] = { /* Sample text for each BandFmt. Used to try to get * the spacing right. */ "888", /* uchar */ "-888", /* char */ "88888", /* ushort */ "-88888", /* short */ "888888888", /* int */ "-888888888", /* uint */ "888888888", /* float */ "(88888888,888888888)", /* complex */ "88888888888", /* double */ "(8888888888,888888888)" /* dpcomplex */ }; Conversion *conv = imagemodel->conv; iImage *iimage = imagemodel->iimage; IMAGE *im = imageinfo_get( FALSE, iimage->value.ii ); double size = (double) im->Ysize * IM_IMAGE_SIZEOF_LINE( im ); unsigned int nb; int fmt; char txt[MAX_LINELENGTH]; VipsBuf buf = VIPS_BUF_STATIC( txt ); #ifdef DEBUG printf( "statusview_changed_cb: %p\n", imagemodel ); #endif /*DEBUG*/ widget_visible( GTK_WIDGET( sv ), imagemodel->show_status ); /* If we're hidden, no need to do any more. */ if( !imagemodel->show_status ) return; if( conv->mag > 0 ) set_glabel( sv->mag, "%s %d:1", _( "Magnification" ), conv->mag ); else set_glabel( sv->mag, "%s 1:%d", _( "Magnification" ), -conv->mag ); vips_buf_appendf( &buf, "%s, ", NN( IOBJECT( iimage )->caption ) ); vips_buf_append_size( &buf, size ); vips_buf_appendf( &buf, ", %.3gx%.3g p/mm", im->Xres, im->Yres ); set_gcaption( sv->top, "%s", vips_buf_all( &buf ) ); if( im->Coding == IM_CODING_LABQ || im->Coding == IM_CODING_RAD ) { nb = 3; fmt = 6; } else { nb = im->Bands; fmt = im->BandFmt; } if( sv->nb != nb || sv->fmt != fmt ) { /* Bands/fmt has changed ... rebuild band display widgets. */ unsigned int i; int width; statusviewband_destroy( sv ); sv->fmt = fmt; sv->nb = nb; if( sv->fmt >= 0 && sv->fmt < IM_NUMBER( sample ) ) width = strlen( sample[sv->fmt] ); else width = 10; /* Don't display more than 8 bands ... it'll make the window * too large. FIXME ... now very kewl */ for( i = 0; i < IM_MIN( 8, nb ); i++ ) { StatusviewBand *band = INEW( NULL, StatusviewBand ); band->sv = sv; band->bandno = i; band->val = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( band->val ), 0.0, 0.5 ); set_fixed( band->val, width ); gtk_box_pack_start( GTK_BOX( sv->hb ), band->val, FALSE, FALSE, 0 ); gtk_widget_show( band->val ); sv->bands = g_slist_append( sv->bands, band ); } } } static void statusview_link( Statusview *sv, Imagemodel *imagemodel ) { sv->imagemodel = imagemodel; g_signal_connect( G_OBJECT( sv->imagemodel ), "changed", G_CALLBACK( statusview_changed_cb ), sv ); } Statusview * statusview_new( Imagemodel *imagemodel ) { Statusview *sv = gtk_type_new( TYPE_STATUSVIEW ); statusview_link( sv, imagemodel ); return( sv ); } /* Turn a IM_CODING_LABQ 4-band image into three floats. */ static void statusview_mouse_LABPACK( Statusview *sv, int x, int y ) { Imagemodel *imagemodel = sv->imagemodel; Conversion *conv = imagemodel->conv; GSList *bands = sv->bands; /* Three widgets we update. */ StatusviewBand *b1; StatusviewBand *b2; StatusviewBand *b3; unsigned char *e = (unsigned char *) get_element( conv->reg, x, y, 0 ); unsigned int iL = (e[0] << 2) | (e[3] >> 6); float L = 100.0 * iL / 1023.0; signed int ia = ((signed char) e[1] << 3) | ((e[3] >> 3) & 0x7); float a = 0.125 * ia; signed int ib = ((signed char) e[2] << 3) | (e[3] & 0x7); float b = 0.125 * ib; if( g_slist_length( sv->bands ) == 3 ) { b1 = (StatusviewBand *) bands->data; b2 = (StatusviewBand *) bands->next->data; b3 = (StatusviewBand *) bands->next->next->data; set_glabel( b1->val, "%g", L ); set_glabel( b2->val, "%g", a ); set_glabel( b3->val, "%g", b ); } } /* Turn a IM_CODING_RAD 4-band image into three floats. */ static void statusview_mouse_RAD( Statusview *sv, int x, int y ) { Imagemodel *imagemodel = sv->imagemodel; Conversion *conv = imagemodel->conv; GSList *bands = sv->bands; /* Three widgets we update. */ StatusviewBand *b1; StatusviewBand *b2; StatusviewBand *b3; unsigned char *e = (unsigned char *) get_element( conv->reg, x, y, 0 ); double f = ldexp( 1.0, e[3] - (128 + 8) ); float r = (e[0] + 0.5) * f; float g = (e[1] + 0.5) * f; float b = (e[2] + 0.5) * f; if( g_slist_length( sv->bands ) == 3 ) { b1 = (StatusviewBand *) bands->data; b2 = (StatusviewBand *) bands->next->data; b3 = (StatusviewBand *) bands->next->next->data; set_glabel( b1->val, "%g", r ); set_glabel( b2->val, "%g", g ); set_glabel( b3->val, "%g", b ); } } /* Sub-fn of below. Remake a band in the bar. */ static void * statusview_mouse_band( StatusviewBand *svb, void *e ) { Imagemodel *imagemodel = svb->sv->imagemodel; Conversion *conv = imagemodel->conv; REGION *reg = conv->reg; IMAGE *im = reg->im; /* Generate string for contents of band element. */ if( im->Coding == IM_CODING_NONE ) switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: set_glabel( svb->val, "%d", ((unsigned char *)e)[svb->bandno] ); break; case IM_BANDFMT_CHAR: set_glabel( svb->val, "%d", ((char *)e)[svb->bandno] ); break; case IM_BANDFMT_USHORT: set_glabel( svb->val, "%d", ((unsigned short *)e)[svb->bandno] ); break; case IM_BANDFMT_SHORT: set_glabel( svb->val, "%d", ((short *)e)[svb->bandno] ); break; case IM_BANDFMT_UINT: set_glabel( svb->val, "%u", ((unsigned int *)e)[svb->bandno] ); break; case IM_BANDFMT_INT: set_glabel( svb->val, "%d", ((int *)e)[svb->bandno] ); break; case IM_BANDFMT_FLOAT: set_glabel( svb->val, "%g", ((float *)e)[svb->bandno] ); break; case IM_BANDFMT_COMPLEX: set_glabel( svb->val, "(%g,%g)", ((float *)e)[svb->bandno << 1], ((float *)e)[(svb->bandno << 1) + 1] ); break; case IM_BANDFMT_DOUBLE: set_glabel( svb->val, "%g", ((double *)e)[svb->bandno] ); break; case IM_BANDFMT_DPCOMPLEX: set_glabel( svb->val, "(%g,%g)", ((double *)e)[svb->bandno << 1], ((double *)e)[(svb->bandno << 1) + 1] ); break; default: set_glabel( svb->val, "???" ); break; } else set_glabel( svb->val, "???" ); return( NULL ); } void statusview_mouse( Statusview *sv, int x, int y ) { Imagemodel *imagemodel = sv->imagemodel; Conversion *conv = imagemodel->conv; IMAGE *im = imageinfo_get( FALSE, conv->ii ); REGION *reg = conv->reg; double dx, dy; x = IM_CLIP( 0, x, conv->underlay.width - 1 ); y = IM_CLIP( 0, y, conv->underlay.height - 1 ); /* Calculate x/y pos we display. */ dx = x; dy = y; if( imagemodel->rulers_offset ) { dx -= im->Xoffset; dy -= im->Yoffset; } if( imagemodel->rulers_mm ) { dx /= im->Xres; dy /= im->Yres; } set_glabel( sv->pos, "(%5g, %5g)", dx, dy ); /* Update value list. */ if( reg ) { if( reg->im->Coding == IM_CODING_LABQ ) statusview_mouse_LABPACK( sv, x, y ); else if( reg->im->Coding == IM_CODING_RAD ) statusview_mouse_RAD( sv, x, y ); else slist_map( sv->bands, (SListMapFn) statusview_mouse_band, get_element( reg, x, y, 0 ) ); } } ================================================ FILE: src/statusview.h ================================================ /* Decls for statusview.c ... display image info and mouse posn */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_STATUSVIEW (statusview_get_type()) #define STATUSVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_STATUSVIEW, Statusview )) #define STATUSVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_STATUSVIEW, StatusviewClass )) #define IS_STATUSVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_STATUSVIEW )) #define IS_STATUSVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_STATUSVIEW )) /* A band element display in the status bar. */ typedef struct _StatusviewBand { Statusview *sv; /* Bar we're in */ int bandno; /* Band we extract */ GtkWidget *val; /* Label we write to */ } StatusviewBand; struct _Statusview { GtkFrame parent_class; Imagemodel *imagemodel; guint changed_sid; GtkWidget *top; /* Top label */ GtkWidget *pos; /* Position */ GtkWidget *hb; /* Band element hbox */ GtkWidget *mag; /* Magnification display */ GSList *bands; /* List of StatusviewBand */ int nb; /* Last number of bands we saw */ int fmt; /* The last bandfmt we set ... for spacing */ }; typedef struct _StatusviewClass { GtkFrameClass parent_class; /* My methods. */ } StatusviewClass; GtkType statusview_get_type( void ); Statusview *statusview_new( Imagemodel *imagemodel ); void statusview_mouse( Statusview *sv, int x, int y ); ================================================ FILE: src/string.c ================================================ /* an editable string */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void string_finalize( GObject *gobject ) { String *string; #ifdef DEBUG printf( "string_finalize\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_STRING( gobject ) ); string = STRING( gobject ); IM_FREE( string->value ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static View * string_view_new( Model *model, View *parent ) { return( stringview_new() ); } /* Members of string we automate. */ static ClassmodelMember string_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( iObject, caption ) }, { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( String, value ) } }; static void string_class_init( StringClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Init methods. */ gobject_class->finalize = string_finalize; model_class->view_new = string_view_new; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = string_members; classmodel_class->n_members = IM_NUMBER( string_members ); } static void string_init( String *string ) { string->value = NULL; IM_SETSTR( string->value, "" ); iobject_set( IOBJECT( string ), CLASS_STRING, NULL ); } GType string_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( StringClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) string_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( String ), 32, /* n_pstringlocs */ (GInstanceInitFunc) string_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "String", &info, 0 ); } return( type ); } ================================================ FILE: src/stringview.c ================================================ /* a view of a text thingy */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static EditviewClass *parent_class = NULL; /* Re-read the text in a tally entry. */ static void * stringview_scan( View *view ) { Stringview *stringview = STRINGVIEW( view ); String *string = STRING( VOBJECT( stringview )->iobject ); Expr *expr = HEAPMODEL( string )->row->expr; char value[MAX_STRSIZE]; char value2[MAX_STRSIZE]; #ifdef DEBUG Row *row = HEAPMODEL( string )->row; printf( "stringview_scan: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ expr_error_clear( expr ); if( !get_geditable_string( EDITVIEW( stringview )->text, value, MAX_STRSIZE ) ) { expr_error_set( expr ); return( view ); } my_strccpy( value2, value ); if( strcmp( string->value, value2 ) != 0 ) { IM_SETSTR( string->value, value2 ); classmodel_update( CLASSMODEL( string ) ) ; } return( VIEW_CLASS( parent_class )->scan( view ) ); } static void stringview_refresh( vObject *vobject ) { Stringview *stringview = STRINGVIEW( vobject ); String *string = STRING( VOBJECT( stringview )->iobject ); #ifdef DEBUG Row *row = HEAPMODEL( string )->row; printf( "stringview_refresh: " ); row_name_print( row ); printf( " (%p)\n", vobject ); #endif /*DEBUG*/ if( string->value ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_appendsc( &buf, FALSE, string->value ); editview_set_entry( EDITVIEW( stringview ), "%s", vips_buf_all( &buf ) ); } VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void stringview_class_init( StringviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = stringview_refresh; view_class->scan = stringview_scan; } static void stringview_init( Stringview *stringview ) { } GtkType stringview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Stringview", sizeof( Stringview ), sizeof( StringviewClass ), (GtkClassInitFunc) stringview_class_init, (GtkObjectInitFunc) stringview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_EDITVIEW, &info ); } return( type ); } View * stringview_new( void ) { Stringview *stringview = gtk_type_new( TYPE_STRINGVIEW ); return( VIEW( stringview ) ); } ================================================ FILE: src/stringview.h ================================================ /* edit a string */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_STRINGVIEW (stringview_get_type()) #define STRINGVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_STRINGVIEW, Stringview )) #define STRINGVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_STRINGVIEW, StringviewClass )) #define IS_STRINGVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_STRINGVIEW )) #define IS_STRINGVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_STRINGVIEW )) typedef struct _Stringview { Editview parent_object; } Stringview; typedef struct _StringviewClass { EditviewClass parent_class; /* My methods. */ } StringviewClass; GtkType stringview_get_type( void ); View *stringview_new( void ); ================================================ FILE: src/subcolumn.c ================================================ /* a subcolumn */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static HeapmodelClass *parent_class = NULL; static gboolean subcolumn_row_pred_none( Row *row ) { return( FALSE ); } /* No params, no super. */ static gboolean subcolumn_row_pred_members( Row *row ) { if( row->sym && is_system( row->sym ) ) return( FALSE ); if( row->sym && is_super( row->sym ) ) return( FALSE ); if( row->sym && row->sym->type == SYM_PARAM ) return( FALSE ); return( TRUE ); } static gboolean subcolumn_row_pred_params( Row *row ) { return( row->sym && row->sym->type == SYM_PARAM ); } /* Everything but empty superclasses. */ static gboolean subcolumn_row_pred_super( Row *row ) { if( row->sym && is_super( row->sym ) && PEISELIST( &row->expr->root ) ) return( FALSE ); return( TRUE ); } /* Array of these guys control member visibility, scol->vislevel indexes this * array, one of preds from vislevel down has to be TRUE for the row to be * visible. */ const SubcolumnVisibility subcolumn_visibility[] = { { "none", subcolumn_row_pred_none }, { "members", subcolumn_row_pred_members }, { "params", subcolumn_row_pred_params }, { "super", subcolumn_row_pred_super } }; const int subcolumn_nvisibility = IM_NUMBER( subcolumn_visibility ); /* Map down a Subcolumn. */ void * subcolumn_map( Subcolumn *scol, row_map_fn fn, void *a, void *b ) { return( icontainer_map( ICONTAINER( scol ), (icontainer_map_fn) fn, a, b ) ); } static void subcolumn_dispose( GObject *gobject ) { Subcolumn *scol; #ifdef DEBUG printf( "subcolumn_dispose\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_SUBCOLUMN( gobject ) ); scol = SUBCOLUMN( gobject ); scol->col = NULL; scol->scol = NULL; scol->top_col = NULL; heap_unregister_element( reduce_context->heap, &scol->base ); scol->base.type = ELEMENT_NOVAL; scol->base.ele = (void *) 13; scol->this = NULL; scol->super = NULL; G_OBJECT_CLASS( parent_class )->dispose( gobject ); } /* Stuff we track during class instance display update. */ typedef struct { Subcolumn *scol; /* Enclosing column */ GSList *notused; /* List of row we've not used */ } ClassRefreshInfo; /* Test for row represents a sym. */ static void * subcolumn_test_sym( Row *row, Symbol *sym ) { if( row->sym == sym ) return( row ); return( NULL ); } /* Test for row has a zombie of the same name. */ static void * subcolumn_test_row_name( Row *row, Symbol *sym ) { if( !row->sym && strcmp( IOBJECT( row )->name, IOBJECT( sym )->name ) == 0 ) return( row ); return( NULL ); } /* Refresh one line of a subcolumn. */ static void subcolumn_class_new_heap_sub( ClassRefreshInfo *cri, Symbol *sym, PElement *value ) { Row *row; #ifdef DEBUG char txt[200]; VipsBuf buf = VIPS_BUF_STATIC( txt ); symbol_qualified_name( sym, &buf ); printf( "subcolumn_class_new_heap_sub: %s\n", vips_buf_all( &buf ) ); #endif /*DEBUG*/ /* Do we have a row for this symbol? */ if( (row = (Row *) slist_map( cri->notused, (SListMapFn) subcolumn_test_sym, sym )) ) { /* Update it. */ if( heapmodel_new_heap( HEAPMODEL( row ), value ) ) expr_error_set( row->expr ); cri->notused = g_slist_remove( cri->notused, row ); } else if( (row = (Row *) slist_map( cri->notused, (SListMapFn) subcolumn_test_row_name, sym )) ) { /* There's a blank row of the same name, left for us by XML * load. Update the row with the correct symbol. */ row_link_symbol( row, sym, NULL ); if( heapmodel_new_heap( HEAPMODEL( row ), value ) ) expr_error_set( row->expr ); cri->notused = g_slist_remove( cri->notused, row ); } else { row = row_new( cri->scol, sym, value ); if( heapmodel_new_heap( HEAPMODEL( row ), value ) ) expr_error_set( row->expr ); } } #ifdef DEBUG static void * subcolumn_class_dump_tiny_row( Row *row ) { row_name_print( row ); printf( " " ); return( NULL ); } #endif /*DEBUG*/ /* A new scrap of heap for a subcolumn. */ static gboolean subcolumn_class_new_heap( Subcolumn *scol, PElement *root ) { PElement instance = *root; Expr *expr; Row *row; gboolean result; PElement base, member; HeapNode *p; ClassRefreshInfo cri; /* Must be a class display. */ g_assert( !scol->is_top ); row = HEAPMODEL( scol )->row; expr = row->expr; #ifdef DEBUG printf( "subcolumn_class_new_heap: " ); row_name_print( row ); printf( "\n" ); #endif /*DEBUG*/ /* Can loop here for some recursive classes. FIXME if( mainw_countdown_animate( 99 ) ) return( FALSE ); */ /* No displays for system rows. */ if( row->sym && is_system( row->sym ) ) return( TRUE ); /* If we are the top of a class instance display, get a new serial. * As we recurse down refreshing our contents, this should stop * circular structures looping the browser. FIXME ... clear flags for a whole class, then do a complete redisplay? more reliable, but even slower :-( */ if( scol->scol->is_top ) heap_serial_new( reduce_context->heap ); /* Is it a class with a typecheck member? Go through * that. Do an isclass first to force eval. */ if( !heap_is_class( &instance, &result ) ) return( FALSE ); if( result && class_get_member( &instance, MEMBER_CHECK, NULL, &member ) ) { #ifdef DEBUG printf( "subcolumn_class_new_heap: invoking arg checker\n" ); #endif /* Force eval of the typecheck member. */ if( !heap_is_class( &member, &result ) || !result ) return( FALSE ); } /* Have we already displayed this class? */ if( (PEGETVAL( &instance )->flgs & FLAG_SERIAL) == reduce_context->heap->serial ) { /* FIXME ... display something here? "circular"? */ return( TRUE ); } SETSERIAL( PEGETVAL( &instance )->flgs, reduce_context->heap->serial ); /* Note the heap root ... if this is the top of a row tree, then we * clone the class and use that private copy. */ PEPOINTE( &base, &(SUBCOLUMN( scol ))->base ); PEPUTPE( &base, &instance ); PEPUTPE( &expr->root, &base ); /* Init rebuild params. We make a list of all the existing * row objects for this class display, and every time we * manage to reuse one of them, we knock it off the list. At the * end, remove all unused rows. */ cri.scol = scol; cri.notused = g_slist_copy( ICONTAINER( scol )->children ); #ifdef DEBUG printf( "subcolumn_class_new_heap: existing rows: " ); icontainer_map( ICONTAINER( scol ), (icontainer_map_fn) subcolumn_class_dump_tiny_row, NULL, NULL ); printf( "\n" ); #endif /*DEBUG*/ /* Loop along the members, updating row entries. */ PEGETCLASSMEMBER( &member, &base ); if( PEISNODE( &member ) ) for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) { PElement s, v; HeapNode *hn; Symbol *sym; /* Get the sym/value pair. */ hn = GETLEFT( p ); PEPOINTLEFT( hn, &s ); PEPOINTRIGHT( hn, &v ); sym = SYMBOL( PEGETSYMREF( &s ) ); /* We don't make rows for the default constructor, or * for ".name". These things don't change, so there's * no point (and the default constructor has no text * equivalent anyway). */ if( strcmp( IOBJECT( sym )->name, MEMBER_NAME ) == 0 ) continue; if( is_member( sym ) && strcmp( IOBJECT( sym )->name, IOBJECT( symbol_get_parent( sym ) )->name ) == 0 ) continue; /* Display! */ subcolumn_class_new_heap_sub( &cri, sym, &v ); } /* Remove all the rows we've not used. */ slist_map( cri.notused, (SListMapFn) iobject_destroy, NULL ); IM_FREEF( g_slist_free, cri.notused ); return( TRUE ); } static void * subcolumn_new_heap( Heapmodel *heapmodel, PElement *root ) { Subcolumn *scol = SUBCOLUMN( heapmodel ); /* New heap for a class display? CLear known_private, we've no idea * where this heap came from. */ if( scol == scol->top_scol ) scol->known_private = FALSE; /* A bunch of locals? Update them all. */ if( !scol->is_top && !subcolumn_class_new_heap( scol, root ) ) return( scol ); return( HEAPMODEL_CLASS( parent_class )->new_heap( heapmodel, root ) ); } static void subcolumn_child_add( iContainer *parent, iContainer *child, int pos ) { Subcolumn *scol = SUBCOLUMN( parent ); Row *row = ROW( child ); /* May not have a symbol yet during ws load. * * Can't use is_this()/is_super(), not everything has been built yet. * We don't do this often, so strcmp() it. */ const char *name = row->sym ? IOBJECT( row->sym )->name : IOBJECT( row )->name; if( strcmp( name, MEMBER_THIS ) == 0 ) scol->this = row; if( strcmp( name, MEMBER_SUPER ) == 0 ) scol->super = row; ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos ); } static void subcolumn_child_remove( iContainer *parent, iContainer *child ) { Subcolumn *scol = SUBCOLUMN( parent ); Row *row = ROW( child ); ICONTAINER_CLASS( parent_class )->child_remove( parent, child ); if( scol->this == row ) scol->this = NULL; if( scol->super == row ) scol->super = NULL; } /* If this is a top-level subcolumn, get the enclosing column. */ static Column * subcolumn_get_column( Subcolumn *scol ) { g_assert( scol->is_top ); return( COLUMN( ICONTAINER( scol )->parent ) ); } /* If this is a nested subcolumn, get the enclosing subcolumn. */ static Subcolumn * subcolumn_get_subcolumn( Subcolumn *scol ) { Rhs *rhs; Row *row; Subcolumn *escol; g_assert( !scol->is_top ); rhs = HEAPMODEL( scol )->rhs; row = HEAPMODEL( rhs )->row; escol = row->scol; return( escol ); } /* Return the enclosing column for a Subcolumn. */ static Column * subcolumn_get_top_column( Subcolumn *scol ) { if( !scol->is_top ) return( subcolumn_get_top_column( subcolumn_get_subcolumn( scol ) ) ); return( subcolumn_get_column( scol ) ); } /* Return the enclosing subcolumn ... but not the is_top one. Ie. the enclosing * subcolumn which has the base for this class tree. */ static Subcolumn * subcolumn_get_top_subcolumn( Subcolumn *scol ) { Subcolumn *enclosing; if( scol->is_top ) return( NULL ); enclosing = subcolumn_get_subcolumn( scol ); if( enclosing->is_top ) return( scol ); else return( subcolumn_get_top_subcolumn( enclosing ) ); } static void subcolumn_parent_add( iContainer *child ) { Subcolumn *scol = SUBCOLUMN( child ); ICONTAINER_CLASS( parent_class )->parent_add( child ); g_assert( IS_COLUMN( child->parent ) || IS_RHS( child->parent ) ); g_assert( !IS_COLUMN( child->parent ) || g_slist_length( child->parent->children ) == 1 ); scol->is_top = IS_COLUMN( child->parent ); /* For sub-columns, default to nothing visible. */ if( !scol->is_top ) scol->vislevel = 0; /* Update context pointers. */ if( scol->is_top ) scol->col = subcolumn_get_column( scol ); else scol->col = NULL; if( !scol->is_top ) scol->scol = subcolumn_get_subcolumn( scol ); else scol->scol = NULL; scol->top_col = subcolumn_get_top_column( scol ); scol->top_scol = subcolumn_get_top_subcolumn( scol ); /* Top level subcolumns default to display on, others to display off. */ MODEL( scol )->display = scol->is_top; } static View * subcolumn_view_new( Model *model, View *parent ) { return( subcolumnview_new() ); } static void subcolumn_display( Model *model, gboolean display ) { /* printf( "subcolumn_display: " ); row_name_print( HEAPMODEL( model )->row ); printf( " %d\n", display ); */ MODEL_CLASS( parent_class )->display( model, display ); } static gboolean subcolumn_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { Subcolumn *scol = SUBCOLUMN( model ); g_assert( IS_COLUMN( parent ) || IS_RHS( parent ) ); if( !get_iprop( xnode, "vislevel", &scol->vislevel ) ) return( FALSE ); if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ) return( FALSE ); return( TRUE ); } static xmlNode * subcolumn_save( Model *model, xmlNode *xnode ) { Subcolumn *scol = SUBCOLUMN( model ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( !set_iprop( xthis, "vislevel", scol->vislevel ) ) return( NULL ); return( xthis ); } static void subcolumn_class_init( SubcolumnClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; HeapmodelClass *heapmodel_class = (HeapmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = subcolumn_dispose; icontainer_class->child_add = subcolumn_child_add; icontainer_class->child_remove = subcolumn_child_remove; icontainer_class->parent_add = subcolumn_parent_add; model_class->view_new = subcolumn_view_new; model_class->display = subcolumn_display; model_class->load = subcolumn_load; model_class->save = subcolumn_save; heapmodel_class->new_heap = subcolumn_new_heap; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void subcolumn_init( Subcolumn *scol ) { #ifdef DEBUG printf( "subcolumn_init\n" ); #endif /*DEBUG*/ scol->col = NULL; scol->scol = NULL; scol->top_col = NULL; scol->vislevel = subcolumn_nvisibility - 1; scol->base.type = ELEMENT_NOVAL; scol->base.ele = (void *) 14; heap_register_element( reduce_context->heap, &scol->base ); scol->known_private = FALSE; scol->this = NULL; scol->super = NULL; } GType subcolumn_get_type( void ) { static GType subcolumn_type = 0; if( !subcolumn_type ) { static const GTypeInfo info = { sizeof( SubcolumnClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) subcolumn_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Subcolumn ), 32, /* n_preallocs */ (GInstanceInitFunc) subcolumn_init, }; subcolumn_type = g_type_register_static( TYPE_HEAPMODEL, "Subcolumn", &info, 0 ); } return( subcolumn_type ); } static void subcolumn_link( Subcolumn *scol, Rhs *rhs, Column *col ) { g_assert( rhs == NULL || col == NULL ); /* parent_add() sets is_top for us. */ if( rhs ) icontainer_child_add( ICONTAINER( rhs ), ICONTAINER( scol ), -1 ); else icontainer_child_add( ICONTAINER( col ), ICONTAINER( scol ), -1 ); } Subcolumn * subcolumn_new( Rhs *rhs, Column *col ) { Subcolumn *scol; scol = SUBCOLUMN( g_object_new( TYPE_SUBCOLUMN, NULL ) ); subcolumn_link( scol, rhs, col ); return( scol ); } void subcolumn_set_vislevel( Subcolumn *scol, int vislevel ) { scol->vislevel = IM_CLIP( 0, vislevel, subcolumn_nvisibility - 1 ); #ifdef DEBUG printf( "subcolumn_set_vislevel: %d\n", scol->vislevel ); #endif /*DEBUG*/ iobject_changed( IOBJECT( scol ) ); } /* Make sure we have a private copy of the graph for this tree of stuff. */ gboolean subcolumn_make_private( Subcolumn *scol ) { Subcolumn *top_scol = scol->top_scol; PElement base; if( !top_scol || top_scol->known_private ) return( TRUE ); #ifdef DEBUG { Row *row = HEAPMODEL( top_scol )->row; printf( "subcolumn_make_private: cloning " ); row_name_print( row ); printf( "\n" ); } #endif /*DEBUG*/ /* Clone from the class args and rebuild our tree. */ PEPOINTE( &base, &top_scol->base ); if( !class_clone_args( reduce_context->heap, &base, &base ) || heapmodel_new_heap( HEAPMODEL( top_scol ), &base ) ) return( FALSE ); top_scol->known_private = TRUE; return( TRUE ); } ================================================ FILE: src/subcolumn.h ================================================ /* a column of rows in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_SUBCOLUMN (subcolumn_get_type()) #define SUBCOLUMN( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_SUBCOLUMN, Subcolumn )) #define SUBCOLUMN_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_SUBCOLUMN, SubcolumnClass)) #define IS_SUBCOLUMN( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_SUBCOLUMN )) #define IS_SUBCOLUMN_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_SUBCOLUMN )) #define SUBCOLUMN_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_SUBCOLUMN, SubcolumnClass )) /* Predicate on a row. */ typedef gboolean (*RowPred)( Row * ); /* Control class member visibility with these. */ typedef struct { const char *name; RowPred pred; } SubcolumnVisibility; struct _Subcolumn { Heapmodel parent_class; /* Our context. */ Column *col; /* Enclosing column (or NULL) */ Subcolumn *scol; /* Enclosing subcolumn (or NULL) */ Column *top_col; /* Topmost enclosing column */ Subcolumn *top_scol; /* Topmost enclosing subcolumn */ int vislevel; /* Visibility level */ gboolean is_top; /* TRUE if parent is a column */ Element base; /* "this" for our members */ gboolean known_private; /* TRUE after top-level clone .. can write! */ /* For subcolumns representing a class instance, the rows for the * "this" and "super" members. */ Row *this; Row *super; }; typedef struct _SubcolumnClass { HeapmodelClass parent_class; /* My methods. */ } SubcolumnClass; extern const SubcolumnVisibility subcolumn_visibility[]; extern const int subcolumn_nvisibility; void *subcolumn_map( Subcolumn *scol, row_map_fn fn, void *a, void *b ); GType subcolumn_get_type( void ); void *subcolumn_new_view( Subcolumn *scol ); Subcolumn *subcolumn_new( Rhs *rhs, Column *col ); void subcolumn_set_vislevel( Subcolumn *scol, int vislevel ); gboolean subcolumn_make_private( Subcolumn *scol ); ================================================ FILE: src/subcolumnview.c ================================================ /* a subcolumnview button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static void * subcolumnview_destroy_sub( Rowview *rview, Subcolumnview *sview ) { DESTROY_GTK( rview ); return( NULL ); } static void subcolumnview_destroy( GtkObject *object ) { Subcolumnview *sview; #ifdef DEBUG printf( "subcolumnview_destroy\n" ); #endif /*DEBUG*/ g_return_if_fail( object != NULL ); g_return_if_fail( IS_SUBCOLUMNVIEW( object ) ); sview = SUBCOLUMNVIEW( object ); UNREF( sview->group ); /* Destroying us won't automatically destroy our rowviews, since they * are not true child-widgets. Do it by hand. */ (void) view_map( VIEW( sview ), (view_map_fn) subcolumnview_destroy_sub, sview, NULL ); DESTROY_GTK( sview->table ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void subcolumnview_link( View *view, Model *model, View *parent ) { Subcolumnview *sview = SUBCOLUMNVIEW( view ); Subcolumn *scol = SUBCOLUMN( model ); #ifdef DEBUG printf( "subcolumnview_link: " ); if( HEAPMODEL( scol )->row ) row_name_print( HEAPMODEL( scol )->row ); else printf( "(null)" ); printf( "\n" ); #endif /*DEBUG*/ VIEW_CLASS( parent_class )->link( view, model, parent ); /* Add to enclosing column, if there is one. Attached to enclosing row * by rowview_refresh() if we're a subcolumn. */ if( !scol->is_top ) sview->rhsview = RHSVIEW( parent ); gtk_widget_show( GTK_WIDGET( sview ) ); } static void * subcolumnview_refresh_sub( Rowview *rview, Subcolumnview *sview ) { Subcolumn *scol = SUBCOLUMN( VOBJECT( sview )->iobject ); Row *row = ROW( VOBJECT( rview )->iobject ); int i; /* Most predicates need a sym. */ if( !row->sym ) return( NULL ); for( i = 0; i <= scol->vislevel; i++ ) if( subcolumn_visibility[i].pred( row ) ) { rowview_set_visible( rview, TRUE ); sview->nvis++; break; } if( i > scol->vislevel ) rowview_set_visible( rview, FALSE ); return( NULL ); } static void subcolumnview_refresh( vObject *vobject ) { Subcolumnview *sview = SUBCOLUMNVIEW( vobject ); Subcolumn *scol = SUBCOLUMN( VOBJECT( sview )->iobject ); int model_rows = icontainer_get_n_children( ICONTAINER( scol ) ); int old_nvis = sview->nvis; gboolean editable = scol->top_col->ws->mode != WORKSPACE_MODE_NOEDIT; #ifdef DEBUG printf( "subcolumnview_refresh\n" ); #endif /*DEBUG*/ if( sview->rows != model_rows ) { sview->rows = model_rows; if( sview->rows ) gtk_table_resize( GTK_TABLE( sview->table ), sview->rows, 4 ); #ifdef DEBUG printf( "subcolumnview_refresh: resize to %d rows\n", sview->rows ); #endif /*DEBUG*/ } /* Top-level subcolumns look different in no-edit mode. */ if( scol->is_top && editable ) { gtk_alignment_set_padding( GTK_ALIGNMENT( sview->align ), 0, 0, 0, 0 ); gtk_table_set_row_spacings( GTK_TABLE( sview->table ), 0 ); gtk_table_set_col_spacings( GTK_TABLE( sview->table ), 0 ); } else if( scol->is_top && !editable ) { gtk_alignment_set_padding( GTK_ALIGNMENT( sview->align ), 5, 5, 5, 5 ); gtk_table_set_row_spacings( GTK_TABLE( sview->table ), 5 ); gtk_table_set_col_spacings( GTK_TABLE( sview->table ), 5 ); } /* Nested subcols: we just change the left indent. */ if( !scol->is_top && editable ) { gtk_alignment_set_padding( GTK_ALIGNMENT( sview->align ), 0, 0, 0, 0 ); } else if( !scol->is_top && !editable ) { gtk_alignment_set_padding( GTK_ALIGNMENT( sview->align ), 0, 0, 15, 0 ); } sview->nvis = 0; (void) view_map( VIEW( sview ), (view_map_fn) subcolumnview_refresh_sub, sview, NULL ); if( sview->nvis != old_nvis ) { view_resize( VIEW( sview ) ); iobject_changed( IOBJECT( scol->top_col ) ); } VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void subcolumnview_class_init( SubcolumnviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass*) class; vObjectClass *vobject_class = (vObjectClass*) class; ViewClass *view_class = (ViewClass*) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = subcolumnview_destroy; /* Create signals. */ /* Init methods. */ vobject_class->refresh = subcolumnview_refresh; view_class->link = subcolumnview_link; } static void subcolumnview_init( Subcolumnview *sview ) { sview->rhsview = NULL; sview->rows = 0; sview->nvis = 0; sview->align = gtk_alignment_new( 0, 0, 1, 1 ); gtk_box_pack_start( GTK_BOX( sview ), sview->align, FALSE, FALSE, 0 ); sview->table = gtk_table_new( sview->rows, 4, FALSE ); gtk_container_add( GTK_CONTAINER( sview->align ), sview->table ); gtk_widget_show_all( sview->align ); sview->group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL ); } GtkType subcolumnview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Subcolumnview", sizeof( Subcolumnview ), sizeof( SubcolumnviewClass ), (GtkClassInitFunc) subcolumnview_class_init, (GtkObjectInitFunc) subcolumnview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VIEW, &info ); } return( type ); } View * subcolumnview_new( void ) { Subcolumnview *sview = gtk_type_new( TYPE_SUBCOLUMNVIEW ); return( VIEW( sview ) ); } ================================================ FILE: src/subcolumnview.h ================================================ /* a column of tallyrows in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_SUBCOLUMNVIEW (subcolumnview_get_type()) #define SUBCOLUMNVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_SUBCOLUMNVIEW, Subcolumnview )) #define SUBCOLUMNVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_SUBCOLUMNVIEW, SubcolumnviewClass )) #define IS_SUBCOLUMNVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_SUBCOLUMNVIEW )) #define IS_SUBCOLUMNVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_SUBCOLUMNVIEW )) struct _Subcolumnview { View view; /* Enclosing rhsview, if any. */ Rhsview *rhsview; /* My instance vars. */ GtkWidget *align; /* Alignment widget */ GtkWidget *table; /* Central tally area for column */ int rows; /* Number of rows atm */ int nvis; /* Number of children currently visible */ GtkSizeGroup *group; /* Align captions with this */ }; typedef struct _SubcolumnviewClass { ViewClass parent_class; /* My methods. */ } SubcolumnviewClass; GtkType subcolumnview_get_type( void ); View *subcolumnview_new( void ); ================================================ FILE: src/symbol.c ================================================ /* Basic ops on symbols. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* All debug #define DEBUG */ /* Just trace create/destroy. #define DEBUG_MAKE */ /* Time recomputes. #define DEBUG_TIME */ /* Show symbols as we recalc #define DEBUG_RECALC */ /* If DEBUG is on, make sure other debugs are on too. */ #ifdef DEBUG # ifndef DEBUG_MAKE # define DEBUG_MAKE # endif # ifndef DEBUG_TIME # define DEBUG_TIME # endif # ifndef DEBUG_RECALC # define DEBUG_RECALC # endif #endif /* Our signals. */ enum { SIG_NEW_VALUE, /* new value for sym->expr */ SIG_LAST }; static guint symbol_signals[SIG_LAST] = { 0 }; /* Global symbol - top-level definitions are locals to this symbol. */ Symbol *symbol_root = NULL; /* Set of dirty top-level symbols with no dirty children which do not contain * errors. Used to generate next-to-recalc. */ static GSList *symbol_leaf_set = NULL; static FilemodelClass *parent_class = NULL; /* Apply a function to a symbol ... and any locals. */ Symbol * symbol_map_all( Symbol *sym, symbol_map_fn fn, void *a, void *b ) { Symbol *res; /* Apply to this sym. */ if( (res = fn( sym, a, b, NULL )) ) return( res ); /* And over any locals of those locals. */ if( sym->expr && sym->expr->compile && (res = icontainer_map3( ICONTAINER( sym->expr->compile ), (icontainer_map3_fn) symbol_map_all, (void *) fn, a, b )) ) return( res ); return( NULL ); } /* Find a symbol's enclosing sym. */ Symbol * symbol_get_parent( Symbol *sym ) { if( !ICONTAINER( sym )->parent ) return( NULL ); return( COMPILE( ICONTAINER( sym )->parent )->sym ); } /* Find the enclosing workspace, if any. */ Workspace * symbol_get_workspace( Symbol *sym ) { if( !sym->expr || !sym->expr->row ) return( NULL ); return( row_get_workspace( sym->expr->row ) ); } /* Find the enclosing tool, if any. */ Tool * symbol_get_tool( Symbol *sym ) { Symbol *i; for( i = sym; i && !i->tool; i = symbol_get_parent( i ) ) ; if( i ) return( i->tool ); return( NULL ); } /* Get the enclosing scope for a sym. */ Symbol * symbol_get_scope( Symbol *sym ) { Symbol *i; for( i = sym; i && !is_scope( i ); i = symbol_get_parent( i ) ) ; return( i ); } /* Make a fully-qualified symbol name .. eg fred.jim, given jim. Don't print * static scopes. */ void symbol_qualified_name( Symbol *sym, VipsBuf *buf ) { Symbol *parent = symbol_get_parent( sym ); if( parent && !is_scope( parent ) ) { symbol_qualified_name( parent, buf ); vips_buf_appends( buf, "." ); } vips_buf_appends( buf, NN( IOBJECT( sym )->name ) ); } /* Make a symbol name relative to a scope context ... ie. from the point of * view of a local of context, what name will find sym. */ void symbol_qualified_name_relative( Symbol *context, Symbol *sym, VipsBuf *buf ) { Symbol *parent = symbol_get_parent( sym ); if( parent && !is_ancestor( context, parent ) ) { symbol_qualified_name_relative( context, parent, buf ); vips_buf_appends( buf, "." ); } vips_buf_appends( buf, NN( IOBJECT( sym )->name ) ); } /* As above, but include stuff about where the symbol is defined, handy for * building error messages. */ void * symbol_name_error( Symbol *sym, VipsBuf *buf ) { Tool *tool; symbol_qualified_name( sym, buf ); if( (tool = symbol_get_tool( sym )) ) tool_error( tool, buf ); vips_buf_appends( buf, " " ); return( NULL ); } /* Handy for error messages ... but nowt else. Return string overwritten on * next call. */ const char * symbol_name( Symbol *sym ) { static char txt[200]; static VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_rewind( &buf ); symbol_qualified_name( sym, &buf ); return( vips_buf_all( &buf ) ); } /* Convenience ... print a qual name to stdout. */ void * symbol_name_print( Symbol *sym ) { printf( "%s ", symbol_name( sym ) ); return( NULL ); } /* Print a symbol's name, including the enclosing static scope. Return value * is a pointer to a static buffer :( */ const char * symbol_name_scope( Symbol *sym ) { Symbol *scope = symbol_get_scope( sym ); static char txt[200]; static VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_rewind( &buf ); vips_buf_appends( &buf, NN( IOBJECT( scope )->name ) ); vips_buf_appends( &buf, "." ); symbol_qualified_name_relative( scope, sym, &buf ); return( vips_buf_all( &buf ) ); } /* Convenience ... print a qual name to stdout. */ void symbol_name_scope_print( Symbol *sym ) { printf( "%s", symbol_name_scope( sym ) ); } void symbol_new_value( Symbol *sym ) { g_signal_emit( G_OBJECT( sym ), symbol_signals[SIG_NEW_VALUE], 0 ); } /* Add a pointer to a patch list. */ void * symbol_patch_add( void **pnt, Symbol *sym ) { g_assert( sym->type == SYM_ZOMBIE ); sym->patch = g_slist_prepend( sym->patch, pnt ); return( NULL ); } static void symbol_clear( Symbol *sym ) { sym->type = SYM_ZOMBIE; sym->patch = NULL; sym->expr = NULL; sym->base.type = ELEMENT_NOVAL; sym->base.ele = (void *) 15; /* handy for debugging */ sym->dirty = FALSE; sym->parents = NULL; sym->topchildren = NULL; sym->topparents = NULL; sym->ndirtychildren = 0; sym->leaf = FALSE; sym->generated = FALSE; sym->placeholder = FALSE; sym->tool = NULL; sym->function = NULL; sym->builtin = NULL; sym->wsr = NULL; sym->ws = NULL; } /* Initialise root symbol. */ Symbol * symbol_root_init( void ) { Symbol *root = SYMBOL( g_object_new( TYPE_SYMBOL, NULL ) ); symbol_clear( root ); iobject_set( IOBJECT( root ), "$$ROOT", NULL ); root->type = SYM_ROOT; root->expr = expr_new( root ); (void) compile_new_local( root->expr ); symbol_root = symbol_new( root->expr->compile, "root" ); symbol_root->type = SYM_ROOT; symbol_root->expr = expr_new( symbol_root ); (void) compile_new( symbol_root->expr ); return( root ); } /* Should a symbol be in the leaf set? */ static gboolean symbol_is_leafable( Symbol *sym ) { if( is_top( sym ) && sym->dirty && sym->expr && !sym->expr->err && sym->ndirtychildren == 0 ) return( TRUE ); return( FALSE ); } #ifdef DEBUG /* Do a sanity check on a symbol. */ void * symbol_sanity( Symbol *sym ) { if( is_top( sym ) ) { if( symbol_ndirty( sym ) != sym->ndirtychildren ) error( "sanity failure #1 for sym \"%s\"", symbol_name( sym ) ); } if( symbol_is_leafable( sym ) && !sym->leaf ) error( "sanity failure #2 for sym \"%s\"", symbol_name( sym ) ); if( !symbol_is_leafable( sym ) && sym->leaf ) error( "sanity failure #3 for sym \"%s\"", symbol_name( sym ) ); if( sym->leaf && !g_slist_find( symbol_leaf_set, sym ) ) error( "sanity failure #6 for sym \"%s\"", symbol_name( sym ) ); if( !sym->leaf && g_slist_find( symbol_leaf_set, sym ) ) error( "sanity failure #7 for sym \"%s\"", symbol_name( sym ) ); return( NULL ); } #endif/*DEBUG*/ #ifdef DEBUG /* Test the leaf set for sanity. */ void symbol_leaf_set_sanity( void ) { slist_map( symbol_leaf_set, (SListMapFn) symbol_sanity, NULL ); icontainer_map( ICONTAINER( symbol_root->expr->compile ), (icontainer_map_fn) symbol_sanity, NULL, NULL ); /* Commented out to reduce spam * printf( "Leaf set: " ); slist_map( symbol_leaf_set, (SListMapFn) dump_tiny, NULL ); printf( "\n" ); */ } #endif /*DEBUG*/ /* Strip a symbol down, ready for redefinition. */ void * symbol_strip( Symbol *sym ) { #ifdef DEBUG_MAKE printf( "symbol_strip: " ); symbol_name_print( sym ); printf( "\n" ); #endif /*DEBUG_MAKE*/ /* Anything that refers to us will need a recomp. */ if( is_top( sym ) ) symbol_dirty_intrans( sym, link_serial_new() ); /* Clean out old exprinfo. */ icontainer_map( ICONTAINER( sym ), (icontainer_map_fn) expr_strip, NULL, NULL ); /* Free any top-links we made. */ (void) slist_map( sym->topchildren, (SListMapFn) link_destroy, NULL ); /* Can free the patch list. We should not have to resolve off this * name again. */ IM_FREEF( g_slist_free, sym->patch ); /* Workspaceroot? Unlink from wsr. */ if( sym->wsr ) { sym->wsr->sym = NULL; sym->wsr = NULL; } /* Workspace? Unlink from ws. */ if( sym->ws ) { sym->ws->sym = NULL; sym->ws = NULL; } /* It's a ZOMBIE now. */ sym->type = SYM_ZOMBIE; #ifdef DEBUG symbol_sanity( sym ); #endif /*DEBUG*/ return( NULL ); } static void * symbol_made_error_clear( Link *link ) { expr_error_clear( link->parent->expr ); return( NULL ); } /* Finish creating a symbol. Sequence is: symbol_new(), specialise ZOMBIE * into a particular symbol type, symbol_made(). Do any final tidying up. */ void symbol_made( Symbol *sym ) { #ifdef DEBUG_MAKE printf( "symbol_made: " ); symbol_name_print( sym ); printf( "\n" ); #endif /*DEBUG_MAKE*/ if( is_top( sym ) ) { /* Remake all top-level dependencies. */ (void) symbol_link_build( sym ); /* Clear error on every symbol that refs us, then mark dirty. * This lets us replace refed-to syms cleanly. */ slist_map( sym->topparents, (SListMapFn) symbol_made_error_clear, NULL ); /* Real dirrrrty. */ if( sym->expr ) expr_dirty( sym->expr, link_serial_new() ); } #ifdef DEBUG dump_symbol( sym ); #endif /*DEBUG*/ } static void * symbol_not_defined_sub( Link *link, VipsBuf *buf ) { symbol_name_error( link->parent, buf ); return( NULL ); } /* Make a "not defined" error message. Can be called before symbol is removed, * so don't assume it's a ZOMBIE. */ void symbol_not_defined( Symbol *sym ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); error_top( _( "Not found." ) ); vips_buf_appendf( &buf, _( "Symbol %s is not defined." ), symbol_name( sym ) ); if( sym->topparents ) { vips_buf_appends( &buf, "\n" ); vips_buf_appendf( &buf, _( "%s is referred to by" ), symbol_name( sym ) ); vips_buf_appends( &buf, ": " ); slist_map2( sym->topparents, (SListMap2Fn) symbol_not_defined_sub, &buf, NULL ); vips_buf_appends( &buf, "\n" ); } error_sub( "%s", vips_buf_all( &buf ) ); } /* Compile refers to sym, which is going ... mark compile as containing an * error. */ static void * symbol_destroy_error( Compile *compile, Symbol *sym ) { symbol_not_defined( sym ); compile_error_set( compile ); return( NULL ); } static void symbol_dispose( GObject *gobject ) { Symbol *sym; Compile *compile; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_SYMBOL( gobject ) ); sym = SYMBOL( gobject ); compile = COMPILE( ICONTAINER( sym )->parent ); #ifdef DEBUG_MAKE printf( "symbol_dispose: " ); symbol_name_print( sym ); printf( "(%p)\n", sym ); #endif /*DEBUG_MAKE*/ /* Make sure we're not leaving last_sym dangling. */ if( compile && compile->last_sym == sym ) compile->last_sym = NULL; /* Clear state. */ if( is_top( sym ) ) { /* All stuff that depends on this sym is now dirty. */ symbol_dirty_intrans( sym, link_serial_new() ); /* This will knock this sym off the leaf set as well. */ symbol_dirty_clear( sym ); } /* Strip it down. */ (void) symbol_strip( sym ); IDESTROY( sym->tool ); /* Any exprs which refer to us must have errors. */ (void) slist_map( sym->parents, (SListMapFn) symbol_destroy_error, sym ); /* Remove links from any expr which refer to us. */ (void) slist_map( sym->parents, (SListMapFn) compile_link_break, sym ); /* No other syms should have toplinks to us. */ (void) slist_map( sym->topparents, (SListMapFn) link_destroy, NULL ); /* Unregister value with GC. */ reduce_unregister( sym ); /* Free other stuff. */ sym->type = SYM_ZOMBIE; g_assert( !sym->tool ); g_assert( !sym->parents ); g_assert( !sym->topparents ); g_assert( !sym->topchildren ); IM_FREEF( g_slist_free, sym->patch ); IM_FREEF( g_slist_free, sym->parents ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void symbol_changed( iObject *iobject ) { Symbol *sym = SYMBOL( iobject ); /* If we have a tool, signal changed on that as well. */ if( sym->tool ) iobject_changed( IOBJECT( sym->tool ) ); IOBJECT_CLASS( parent_class )->changed( iobject ); } static void symbol_real_new_value( Symbol *symbol ) { } static void symbol_class_init( SymbolClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = (iObjectClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = symbol_dispose; iobject_class->changed = symbol_changed; symbol_signals[SIG_NEW_VALUE] = g_signal_new( "new_value", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( SymbolClass, new_value ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); class->new_value = symbol_real_new_value; } static void symbol_init( Symbol *sym ) { symbol_clear( sym ); #ifdef DEBUG_MAKE printf( "symbol_init: (%p)\n", sym ); #endif /*DEBUG_MAKE*/ } GtkType symbol_get_type( void ) { static GtkType symbol_type = 0; if( !symbol_type ) { static const GTypeInfo info = { sizeof( SymbolClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) symbol_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Symbol ), 32, /* n_preallocs */ (GInstanceInitFunc) symbol_init, }; symbol_type = g_type_register_static( TYPE_FILEMODEL, "Symbol", &info, 0 ); } return( symbol_type ); } /* Make a new symbol on an expr. If it's already there and a ZOMBIE, just * return it. If it's not a ZOMBIE, turn it into one. Otherwise make and * link on a new symbol. */ Symbol * symbol_new( Compile *compile, const char *name ) { Symbol *sym; if( (sym = compile_lookup( compile, name )) ) { if( sym->type != SYM_ZOMBIE ) /* Already exists: strip it down. */ (void) symbol_strip( sym ); #ifdef DEBUG_MAKE printf( "symbol_new: redefining " ); symbol_name_print( sym ); printf( "(%p)\n", sym ); #endif /*DEBUG_MAKE*/ } else { sym = SYMBOL( g_object_new( TYPE_SYMBOL, NULL ) ); iobject_set( IOBJECT( sym ), name, NULL ); icontainer_child_add( ICONTAINER( compile ), ICONTAINER( sym ), -1 ); #ifdef DEBUG_MAKE printf( "symbol_new: creating " ); symbol_name_print( sym ); printf( "(%p)\n", sym ); #endif /*DEBUG_MAKE*/ } return( sym ); } gboolean symbol_rename( Symbol *sym, const char *new_name ) { Compile *compile = COMPILE( ICONTAINER( sym )->parent ); Symbol *old_sym; if( strcmp( IOBJECT( sym )->name, new_name ) == 0 ) return( TRUE ); if( (old_sym = compile_lookup( compile, new_name )) ) { error_top( "%s", _( "Name in use." ) ); error_sub( _( "Can't rename %s \"%s\" as \"%s\". " "The name is already in use." ), decode_SymbolType_user( sym->type ), IOBJECT( sym )->name, new_name ); return( FALSE ); } /* Everything that depends on us will break. */ symbol_dirty_intrans( sym, link_serial_new() ); g_object_ref( sym ); icontainer_child_remove( ICONTAINER( sym ) ); iobject_set( IOBJECT( sym ), new_name, NULL ); icontainer_child_add( ICONTAINER( compile ), ICONTAINER( sym ), ICONTAINER( sym )->pos ); g_object_unref( sym ); return( TRUE ); } void symbol_error_redefine( Symbol *sym ) { static char txt[200]; static VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_rewind( &buf ); vips_buf_appendf( &buf, _( "Redefinition of \"%s\"." ), IOBJECT( sym )->name ); if( sym->tool && sym->tool->lineno != -1 ) { vips_buf_appendf( &buf, "\n" ); vips_buf_appendf( &buf, _( "Previously defined at line %d." ), sym->tool->lineno ); } yyerror( vips_buf_all( &buf ) ); } /* Name in defining occurence. If this is a top-level definition, clean the * old symbol and get ready to attach a user function to it. If its not a top- * level definition, we flag an error. Consider repeated parameter names, * repeated occurence of names in locals, local name clashes with parameter * name etc. * We make a ZOMBIE: our caller should turn it into a blank user definition, a * parameter etc. */ Symbol * symbol_new_defining( Compile *compile, const char *name ) { Symbol *sym; /* Block definition of "root" anywhere ... too confusing. */ if( strcmp( name, IOBJECT( symbol_root )->name ) == 0 ) nip2yyerror( _( "Attempt to redefine root symbol \"%s\"." ), name ); /* Is this a redefinition of an existing symbol? */ if( (sym = compile_lookup( compile, name )) ) { /* Yes. Check that this redefinition is legal. */ switch( sym->type ) { case SYM_VALUE: /* Redef of existing symbol? Only allowed at top * level. */ if( !is_scope( compile->sym ) ) symbol_error_redefine( sym ); break; case SYM_ZOMBIE: /* This is the definition for a previously referenced * symbol. Just return the ZOMBIE we made. */ break; default: /* Parameter, workspace, etc. */ nip2yyerror( _( "Can't redefine %s \"%s\"." ), decode_SymbolType_user( sym->type ), name ); /*NOTREACHED*/ } /* This is the defining occurence ... move to the end of the * traverse order. */ icontainer_child_move( ICONTAINER( sym ), -1 ); } /* Get it ready. */ sym = symbol_new( compile, name ); return( sym ); } /* Make a reference to a symbol. Look on the local table for the name - if * it's not there, make a ZOMBIE. Note that ZOMBIEs etc. need patch lists * attached to them for all pointers to them we make. Responsibility of * caller! */ Symbol * symbol_new_reference( Compile *compile, const char *name ) { Symbol *sym = compile_lookup( compile, name ); if( !sym ) sym = symbol_new( compile, name ); /* Note the new dependency. */ compile_link_make( compile, sym ); return( sym ); } /* Compile refers to child ... break link. */ void * symbol_link_break( Symbol *child, Compile *compile ) { compile_link_break( compile, child ); return( NULL ); } /* Specialise into a user definition. */ gboolean symbol_user_init( Symbol *sym ) { g_assert( sym->type == SYM_ZOMBIE ); sym->type = SYM_VALUE; reduce_register( sym ); if( !sym->expr ) sym->expr = expr_new( sym ); /* We don't symbol_made() yet, wait until we have finished building * sym->expr. */ return( TRUE ); } /* Specialise into a parameter on an expression. */ gboolean symbol_parameter_init( Symbol *sym ) { Compile *parent = COMPILE( ICONTAINER( sym )->parent ); g_assert( sym->type == SYM_ZOMBIE ); sym->type = SYM_PARAM; parent->param = g_slist_append( parent->param, sym ); parent->nparam = g_slist_length( parent->param ); symbol_made( sym ); return( TRUE ); } /* Specialise into a builtin parameter (eg. "this"). */ gboolean symbol_parameter_builtin_init( Symbol *sym ) { g_assert( sym->type == SYM_ZOMBIE ); sym->type = SYM_PARAM; symbol_made( sym ); return( TRUE ); } /* Get the next dirty leaf symbol. */ static Symbol * symbol_leaf_next( void ) { if( symbol_leaf_set ) return( (Symbol *) symbol_leaf_set->data ); else return( NULL ); } /* Are there symbols we can recalculate? Used to display "Calculating ..." * status. */ gboolean symbol_busy( void ) { return( symbol_leaf_set != NULL ); } /* Set leaf state. */ static void symbol_set_leaf( Symbol *sym, gboolean leaf ) { if( sym->leaf != leaf ) { gboolean changed; sym->leaf = leaf; changed = FALSE; if( leaf ) { if( !symbol_leaf_set ) changed = TRUE; symbol_leaf_set = g_slist_prepend( symbol_leaf_set, sym ); } else { g_assert( symbol_leaf_set ); symbol_leaf_set = g_slist_remove( symbol_leaf_set, sym ); if( !symbol_leaf_set ) changed = TRUE; } if( changed ) iobject_changed( IOBJECT( reduce_context->heap ) ); if( sym->expr && sym->expr->row ) iobject_changed( IOBJECT( sym->expr->row ) ); } } /* State of a symbol has changed ... update! */ void symbol_state_change( Symbol *sym ) { g_assert( sym->ndirtychildren >= 0 ); /* Used to do more ... now we just set leaf. */ symbol_set_leaf( sym, symbol_is_leafable( sym ) ); } /* Recalculate a symbol. We know we are dirty and have no dirty ancestors. */ static gboolean symbol_recalculate_sub( Symbol *sym ) { gboolean result = TRUE; #ifdef DEBUG_TIME static GTimer *timer = NULL; if( !timer ) timer = g_timer_new(); g_timer_reset( timer ); #endif /*DEBUG_TIME*/ g_assert( is_value( sym ) ); if( sym->expr->row ) { /* This is the root of a display ... use that recomp * mechanism. */ row_recomp( sym->expr->row ); /* Stuff may have been removed. */ if( sym->expr && sym->expr->row && sym->expr->row->err ) result = FALSE; } else if( sym->expr->compile->nparam == 0 ) { /* No params: this ought to have a value. */ if( !reduce_regenerate( sym->expr, &sym->expr->root ) ) result = FALSE; } #ifdef DEBUG_TIME printf( "symbol_recalculate_sub: " ); symbol_name_scope_print( sym ); printf( " %g\n", g_timer_elapsed( timer, NULL ) ); #endif /*DEBUG_TIME*/ return( result ); } /* Note the name of the last thing we calced here, for progress to display. */ static char symbol_last_calc_txt[256]; static VipsBuf symbol_last_calc_buf = VIPS_BUF_STATIC( symbol_last_calc_txt ); static void symbol_note_calc_name( Symbol *sym ) { Symbol *scope = symbol_get_scope( sym ); VipsBuf *buf = &symbol_last_calc_buf; vips_buf_rewind( buf ); vips_buf_appends( buf, NN( IOBJECT( scope )->name ) ); vips_buf_appends( buf, "." ); symbol_qualified_name_relative( scope, sym, buf ); } const char * symbol_get_last_calc( void ) { return( vips_buf_all( &symbol_last_calc_buf ) ); } /* We can get called recursively .. eg. we do an im_tiff2vips(), that * pops a progress box, that triggers idle, that tries to recalc a * leaf again. */ static gboolean symbol_running = FALSE; /* Recalc a symbol ... with error checks. */ static void * symbol_recalculate_leaf_sub( Symbol *sym ) { #ifdef DEBUG_RECALC printf( "symbol_recalculate_leaf_sub: %s\n", symbol_name_scope( sym ) ); /* We can symbol_recalculate_leaf_sub() syms which are not dirty. */ g_assert( !sym->dirty || symbol_is_leafable( sym ) ); g_assert( symbol_ndirty( sym ) == 0 ); #endif /*DEBUG_RECALC*/ error_clear(); if( sym->expr->err ) { expr_error_get( sym->expr ); #ifdef DEBUG_RECALC printf( "\t(error: previous error)\n" ); #endif /*DEBUG_RECALC*/ return( sym ); } if( !sym->dirty ) return( NULL ); if( !is_value( sym ) ) { symbol_dirty_clear( sym ); return( NULL ); } if( symbol_running ) return( NULL ); reduce_context->heap->filled = FALSE; symbol_running = TRUE; progress_begin(); symbol_note_calc_name( sym ); if( !symbol_recalculate_sub( sym ) || reduce_context->heap->filled ) { expr_error_set( sym->expr ); symbol_running = FALSE; progress_end(); #ifdef DEBUG_RECALC printf( "\t(error: %s %s)\n", sym->expr->error_top, sym->expr->error_sub ); #endif /*DEBUG_RECALC*/ return( sym ); } symbol_running = FALSE; progress_end(); /* Have we discovered any dirty children? If not, we've cleaned this * sym. */ if( !sym->ndirtychildren ) { symbol_dirty_clear( sym ); if( sym->expr ) { expr_new_value( sym->expr ); #ifdef DEBUG_RECALC printf( "\tsuccess: " ); graph_pointer( &sym->expr->root ); #endif /*DEBUG_RECALC*/ } } #ifdef DEBUG_RECALC else { printf( "\t(found dirty children)\n" ); } #endif /*DEBUG_RECALC*/ return( NULL ); } /* Recalculate a symbol. FALSE if no symbols can be recalced. */ static gboolean symbol_recalculate_leaf( void ) { gboolean recalculated; Symbol *sym; recalculated = FALSE; #ifdef DEBUG printf( "symbol_recalculate_leaves: Leaf set: " ); slist_map( symbol_leaf_set, (SListMapFn) dump_tiny, NULL ); printf( "\n" ); #endif /*DEBUG*/ /* Grab stuff off the leaf set. */ if( (sym = symbol_leaf_next()) ) { /* Should be dirty with no dirty children. Unless it's a * function, in which case dirty kids are OK. */ g_assert( sym->dirty ); g_assert( !sym->expr->err ); g_assert( is_top( sym ) ); g_assert( symbol_ndirty( sym ) == 0 || is_value( sym ) ); /* Found a symbol! */ (void) symbol_recalculate_leaf_sub( sym ); /* Note a pending GC. */ (void) heap_gc_request( reduce_context->heap ); /* We have recalculated a symbol. */ recalculated = TRUE; } return( recalculated ); } /* Our idle recomp callback. */ static gint symbol_idle_id = 0; static gboolean symbol_recalculate_idle_cb( void ) { static GTimer *timer = NULL; gboolean run_again; #ifdef DEBUG_RECALC printf( "symbol_recalculate_idle_cb:\n" ); #endif /*DEBUG_RECALC*/ if( symbol_running ) /* We've been run from a nested main loop, perhaps from the * progress bar. Just run again and perhaps next time we'll be * back in the top-level main loop. */ return( TRUE ); if( !timer ) timer = g_timer_new(); g_timer_reset( timer ); run_again = TRUE; if( !mainw_auto_recalc ) /* Auto-calc has been turned off during a recomp. */ run_again = FALSE; else while( g_timer_elapsed( timer, NULL ) < 0.1 ) if( !symbol_recalculate_leaf() ) { run_again = FALSE; break; } if( !run_again ) { #ifdef DEBUG_RECALC printf( "symbol_recalculate_idle_cb: bg recalc done\n" ); #endif /*DEBUG_RECALC*/ symbol_idle_id = 0; progress_end(); } return( run_again ); } /* Recalculate ... either nudge the idle recomp, or in batch mode, do a recomp * right now. */ void symbol_recalculate_all_force( gboolean now ) { #ifdef DEBUG icontainer_map( ICONTAINER( symbol_root->expr->compile ), (icontainer_map_fn) symbol_sanity, NULL, NULL ); #endif /*DEBUG*/ /* In case we're called directly. */ (void) view_scan_all(); if( symbol_running ) /* Do nothing. */ ; else if( main_option_batch || now ) { progress_begin(); while( symbol_recalculate_leaf() ) ; progress_end(); } else if( !symbol_idle_id ) { #ifdef DEBUG_RECALC printf( "symbol_recalculate_all_force: " "starting bg recalc ...\n" ); #endif /*DEBUG_RECALC*/ progress_begin(); symbol_idle_id = g_idle_add( (GSourceFunc) symbol_recalculate_idle_cb, NULL ); } } /* Recalculate the symbol table. */ void symbol_recalculate_all( void ) { /* Do a scan, even if we don't recomp. We need to pick up edits before * views get refreshed. */ (void) view_scan_all(); if( mainw_auto_recalc ) symbol_recalculate_all_force( FALSE ); } /* Recalc a symbol ... with error checks. */ gboolean symbol_recalculate_check( Symbol *sym ) { gboolean result; result = symbol_recalculate_leaf_sub( sym ) == NULL; return( result ); } ================================================ FILE: src/symbol.h ================================================ /* Types for the symbol table. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_SYMBOL (symbol_get_type()) #define SYMBOL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_SYMBOL, Symbol )) #define SYMBOL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_SYMBOL, SymbolClass)) #define IS_SYMBOL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_SYMBOL )) #define IS_SYMBOL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_SYMBOL )) #define SYMBOL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_SYMBOL, SymbolClass )) /* The types of symbol we can have. */ typedef enum { SYM_VALUE, /* Symbol with a value attached */ SYM_PARAM, /* A parameter to a user function */ SYM_ZOMBIE, /* A referred to but not defined */ SYM_WORKSPACE, /* A loaded workspace */ SYM_WORKSPACEROOT, /* Base of all workspaces */ SYM_ROOT, /* The root symbol */ SYM_EXTERNAL, /* A reference to an external function */ SYM_BUILTIN /* A reference to a built-in function */ } SymbolType; /* A symbol. */ struct _Symbol { Filemodel parent_class; /* The type of this symbol. */ SymbolType type; /* Track during parse. A list of pointers to pointers to this * symbol which we need to patch if we resolve to an outer scope. */ GSList *patch; /* Main expression for this sym. All expressions are icontainer * children of us. */ Expr *expr; /* Base of graph for value of this symbol. Use sym->expr->root to get * value though .. we just hold pointer for GC here. Expressions on * ext_expr have their GC handled by their enclosing Subcolumn. */ Element base; /* Value for this expr */ /* Recomputation links. Use these to work out what to build next. */ gboolean dirty; /* True if this sym needs recalc */ GSList *parents; /* Compiles which refer to this sym */ GSList *topchildren; /* For top syms, all top-level children */ GSList *topparents; /* For top syms, all top-level parents */ int ndirtychildren; /* Number of dirty top syms we refer to */ gboolean leaf; /* True for in recomp set */ /* This is a generated symbol, like $$result, $$fn1, whatever. */ gboolean generated; /* A temporary intermediate symbol generated during parse to hold * stuff until we need it. Don't generate code for these. */ gboolean placeholder; /* X-tras for definitions. */ Tool *tool; /* Tool and toolkit defined in */ /* X-tras for SYM_EXTERNAL ... our im_function. */ im_function *function; /* Function we run */ int fn_nargs; /* Number of args fn needs from nip */ /* X-tras for SYM_BUILTIN ... our function. */ BuiltinInfo *builtin; /* For WORKSPACEROOT ... the wsr we represent. */ Workspaceroot *wsr; /* For WORKSPACE ... the ws we represent. */ Workspace *ws; }; typedef struct _SymbolClass { FilemodelClass parent_class; /* new_value sym->expr has a new value (this signal is fwd'd from sym->expr) */ void (*new_value)( Symbol *sym ); } SymbolClass; GType symbol_get_type( void ); /* All symbols come off this. */ extern Symbol *symbol_root; Symbol *symbol_map_all( Symbol *sym, symbol_map_fn fn, void *a, void *b ); Symbol *symbol_get_parent( Symbol *sym ); Workspace *symbol_get_workspace( Symbol *sym ); Tool *symbol_get_tool( Symbol *sym ); Symbol *symbol_get_scope( Symbol *sym ); void symbol_qualified_name( Symbol *sym, VipsBuf *buf ); void symbol_qualified_name_relative( Symbol *context, Symbol *sym, VipsBuf *buf ); void *symbol_name_error( Symbol *sym, VipsBuf *buf ); const char *symbol_name( Symbol *sym ); void *symbol_name_print( Symbol *sym ); const char *symbol_name_scope( Symbol *sym ); void symbol_name_scope_print( Symbol *sym ); void symbol_new_value( Symbol *sym ); void *symbol_patch_add( void **pnt, Symbol *sym ); Symbol *symbol_root_init( void ); Symbol *symbol_new( Compile *compile, const char *name ); gboolean symbol_rename( Symbol *sym, const char *new_name ); void symbol_error_redefine( Symbol *sym ); Symbol *symbol_new_defining( Compile *compile, const char *name ); Symbol *symbol_new_reference( Compile *compile, const char *name ); void symbol_made( Symbol *sym ); void symbol_not_defined( Symbol *sym ); void *symbol_link_break( Symbol *child, Compile *compile ); gboolean symbol_user_init( Symbol *sym ); gboolean symbol_parameter_init( Symbol *sym ); gboolean symbol_parameter_builtin_init( Symbol *sym ); gboolean symbol_busy( void ); void *symbol_sanity( Symbol *sym ); void symbol_leaf_set_sanity( void ); void *symbol_strip( Symbol *sym ); void symbol_state_change( Symbol *sym ); const char *symbol_get_last_calc( void ); gboolean symbol_recalculate_check( Symbol *sym ); void symbol_recalculate_all_force( gboolean now ); void symbol_recalculate_all( void ); ================================================ FILE: src/toggle.c ================================================ /* a toggle button ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static ClassmodelClass *parent_class = NULL; static View * toggle_view_new( Model *model, View *parent ) { return( toggleview_new() ); } /* Members of toggle we automate. */ static ClassmodelMember toggle_members[] = { { CLASSMODEL_MEMBER_STRING, NULL, 0, MEMBER_CAPTION, "caption", N_( "Caption" ), G_STRUCT_OFFSET( iObject, caption ) }, { CLASSMODEL_MEMBER_BOOLEAN, NULL, 0, MEMBER_VALUE, "value", N_( "Value" ), G_STRUCT_OFFSET( Toggle, value ) } }; static void toggle_class_init( ToggleClass *class ) { ModelClass *model_class = (ModelClass *) class; ClassmodelClass *classmodel_class = (ClassmodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ model_class->view_new = toggle_view_new; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); classmodel_class->members = toggle_members; classmodel_class->n_members = IM_NUMBER( toggle_members ); } static void toggle_init( Toggle *toggle ) { toggle->value = FALSE; iobject_set( IOBJECT( toggle ), CLASS_TOGGLE, NULL ); } GType toggle_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ToggleClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) toggle_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Toggle ), 32, /* n_preallocs */ (GInstanceInitFunc) toggle_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Toggle", &info, 0 ); } return( type ); } ================================================ FILE: src/toggle.h ================================================ /* a toggle button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOGGLE (toggle_get_type()) #define TOGGLE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_TOGGLE, Toggle )) #define TOGGLE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_TOGGLE, ToggleClass)) #define IS_TOGGLE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_TOGGLE )) #define IS_TOGGLE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_TOGGLE )) #define TOGGLE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_TOGGLE, ToggleClass )) typedef struct _Toggle { Classmodel parent_class; /* My instance vars. */ gboolean value; } Toggle; typedef struct _ToggleClass { ClassmodelClass parent_class; /* My methods. */ } ToggleClass; GType toggle_get_type( void ); ================================================ FILE: src/toggleview.c ================================================ /* the display part of a toggle button */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static GraphicviewClass *parent_class = NULL; /* Toggleview callback. */ static void toggleview_change_cb( GtkWidget *widget, Toggleview *togview ) { Toggle *tog = TOGGLE( VOBJECT( togview )->iobject ); Classmodel *classmodel = CLASSMODEL( tog ); if( tog->value != GTK_TOGGLE_BUTTON( widget )->active ) { tog->value = GTK_TOGGLE_BUTTON( widget )->active; classmodel_update( classmodel ); symbol_recalculate_all(); } } static void toggleview_refresh( vObject *vobject ) { Toggleview *togview = TOGGLEVIEW( vobject ); Toggle *tog = TOGGLE( VOBJECT( togview )->iobject ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togview->toggle ), tog->value ); set_glabel( GTK_BIN( togview->toggle )->child, "%s", IOBJECT( tog )->caption ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void toggleview_class_init( ToggleviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = toggleview_refresh; } static void toggleview_init( Toggleview *togview ) { togview->toggle = build_gtoggle( GTK_WIDGET( togview ), "" ); set_tooltip( togview->toggle, _( "Left-click to change value" ) ); gtk_signal_connect( GTK_OBJECT( togview->toggle ), "clicked", GTK_SIGNAL_FUNC( toggleview_change_cb ), togview ); gtk_widget_show_all( GTK_WIDGET( togview ) ); } GtkType toggleview_get_type( void ) { static GtkType toggleview_type = 0; if( !toggleview_type ) { static const GtkTypeInfo info = { "Toggleview", sizeof( Toggleview ), sizeof( ToggleviewClass ), (GtkClassInitFunc) toggleview_class_init, (GtkObjectInitFunc) toggleview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; toggleview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( toggleview_type ); } View * toggleview_new( void ) { Toggleview *togview = gtk_type_new( TYPE_TOGGLEVIEW ); return( VIEW( togview ) ); } ================================================ FILE: src/toggleview.h ================================================ /* a toggleview button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOGGLEVIEW (toggleview_get_type()) #define TOGGLEVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_TOGGLEVIEW, Toggleview )) #define TOGGLEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_TOGGLEVIEW, ToggleviewClass )) #define IS_TOGGLEVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_TOGGLEVIEW )) #define IS_TOGGLEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_TOGGLEVIEW )) typedef struct _Toggleview { Graphicview parent_object; /* My instance vars. */ GtkWidget *toggle; } Toggleview; typedef struct _ToggleviewClass { GraphicviewClass parent_class; /* My methods. */ } ToggleviewClass; GtkType toggleview_get_type( void ); View *toggleview_new( void ); ================================================ FILE: src/tool.c ================================================ /* Manage toolkits and their display. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_VERBOSE #define DEBUG_MENUS #define DEBUG #define DEBUG_TOOLITEM */ #include "ip.h" static FilemodelClass *parent_class = NULL; /* Largest string we let the user set for name/tip/etc. */ #define MAX_NAME (256) void tool_error( Tool *tool, VipsBuf *buf ) { if( tool->lineno != -1 ) { vips_buf_appends( buf, " (" ); if( FILEMODEL( tool->kit )->filename ) vips_buf_appends( buf, FILEMODEL( tool->kit )->filename ); else vips_buf_appends( buf, IOBJECT( tool->kit )->name ); vips_buf_appendf( buf, ":%d)", tool->lineno ); } } static void * tool_linkreport_sym_sym( Symbol *child, Symbol *parent, VipsBuf *buf, gboolean *found ) { /* Don't report generated syms eg. from lcomps or pattern * matches. */ if( child->type == SYM_ZOMBIE && !child->generated && !parent->generated && !compile_resolve_top( child ) ) { symbol_name_error( parent, buf ); vips_buf_appendf( buf, " " ); /* used as in "fred refers to undefined symbol jim" */ vips_buf_appendf( buf, _( "refers to undefined symbol" ) ); vips_buf_appendf( buf, " " ); symbol_qualified_name( child, buf ); vips_buf_appendf( buf, "\n" ); *found = TRUE; } return( NULL ); } static void * tool_linkreport_sym( Symbol *sym, VipsBuf *buf, gboolean *found ) { if( sym->expr ) return( slist_map3( sym->expr->compile->children, (SListMap3Fn) tool_linkreport_sym_sym, sym, buf, found ) ); return( NULL ); } void * tool_linkreport_tool( Tool *tool, VipsBuf *buf, gboolean *found ) { if( tool->type != TOOL_SYM ) return( NULL ); return( symbol_map_all( tool->sym, (symbol_map_fn) tool_linkreport_sym, buf, found ) ); } static void tool_finalize( GObject *gobject ) { Tool *tool; #ifdef DEBUG printf( "tool_finalize: %p %s\n", gobject, NN( IOBJECT( gobject )->name ) ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_TOOL( gobject ) ); tool = TOOL( gobject ); IM_FREE( tool->help ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void *toolitem_free( Toolitem *toolitem ); /* Remove a tool. Also strip the sym, if any. */ static void tool_dispose( GObject *gobject ) { Tool *tool = TOOL( gobject ); #ifdef DEBUG printf( "tool_dispose: destroying tool for " ); if( tool->sym ) symbol_name_print( tool->sym ); else printf( "anonymous-tool" ); printf( "at addr %p\n", tool ); #endif /*DEBUG*/ FREESID( tool->new_value_sid, tool->link_sym ); /* Unlink from symbol and toolkit. This changes the kit - mark it as * dirty. */ if( tool->sym ) { Symbol *sym = tool->sym; sym->tool = NULL; tool->sym = NULL; symbol_strip( sym ); /* Anything that referred to this symbol is going to need a * recalc. */ } if( tool->kit ) { filemodel_set_modified( FILEMODEL( tool->kit ), TRUE ); tool->kit = NULL; } IM_FREEF( toolitem_free, tool->toolitem ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static View * tool_view_new( Model *model, View *parent ) { return( toolview_new() ); } /* Save a tool's definition to a file. */ static gboolean tool_save_text( Model *model, iOpenFile *of ) { Tool *tool = TOOL( model ); Symbol *sym = tool->sym; switch( tool->type ) { case TOOL_SYM: if( sym->expr ) if( !ifile_write( of, "%s;\n\n", sym->expr->compile->text ) ) return( FALSE ); break; case TOOL_SEP: if( !ifile_write( of, "#separator\n\n" ) ) return( FALSE ); break; case TOOL_DIA: if( !ifile_write( of, "#dialog \"%s\" \"%s\"\n\n", IOBJECT( tool )->name, FILEMODEL( tool )->filename ) ) return( FALSE ); break; default: g_assert( FALSE ); } return( TRUE ); } static char * tool_type_to_char( Tooltype type ) { switch( type ) { case TOOL_SYM: return( "symbol" ); case TOOL_DIA: return( "dialog" ); case TOOL_SEP: return( "separator" ); default: g_assert( FALSE ); /* Keep gcc happy. */ return( FALSE ); } } static void tool_info( iObject *iobject, VipsBuf *buf ) { Tool *tool = TOOL( iobject ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); vips_buf_appendf( buf, "type = \"%s\"\n", tool_type_to_char( tool->type ) ); if( tool->type == TOOL_SYM ) vips_buf_appendf( buf, "symbol = \"%s\"\n", IOBJECT( tool->sym )->name ); if( tool->lineno != -1 ) vips_buf_appendf( buf, "lineno = %d\n", tool->lineno ); if( tool->kit ) vips_buf_appendf( buf, "toolkit = \"%s\"\n", IOBJECT( tool->kit )->name ); } static void tool_parent_add( iContainer *child ) { Tool *tool = TOOL( child ); Toolkit *kit = TOOLKIT( child->parent ); tool->kit = kit; ICONTAINER_CLASS( parent_class )->parent_add( child ); } static void tool_class_init( ToolClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->finalize = tool_finalize; gobject_class->dispose = tool_dispose; iobject_class->info = tool_info; icontainer_class->parent_add = tool_parent_add; model_class->view_new = tool_view_new; model_class->save_text = tool_save_text; } static void tool_init( Tool *tool ) { tool->type = TOOL_SEP; tool->sym = NULL; tool->kit = NULL; tool->lineno = -1; } GType tool_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ToolClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) tool_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Tool ), 32, /* n_preallocs */ (GInstanceInitFunc) tool_init, }; type = g_type_register_static( TYPE_FILEMODEL, "Tool", &info, 0 ); } return( type ); } /* Add a tool to a toolkit. */ static void tool_link( Tool *tool, Toolkit *kit, int pos, const char *name ) { #ifdef DEBUG printf( "tool_link: %s\n", name ); #endif /*DEBUG*/ filemodel_set_modified( FILEMODEL( kit ), TRUE ); iobject_set( IOBJECT( tool ), name, NULL ); icontainer_child_add( ICONTAINER( kit ), ICONTAINER( tool ), pos ); } static void * toolitem_free( Toolitem *toolitem ) { Toolitem *parent = toolitem->parent; #ifdef DEBUG_TOOLITEM printf( "toolitem_free: %s\n", toolitem->name ); #endif /*DEBUG_TOOLITEM*/ slist_map( toolitem->children, (SListMapFn) toolitem_free, NULL ); g_assert( !toolitem->children ); if( parent ) { parent->children = g_slist_remove( parent->children, toolitem ); toolitem->parent = NULL; } IM_FREE( toolitem->label ); IM_FREE( toolitem->name ); IM_FREE( toolitem->icon ); IM_FREE( toolitem->tooltip ); IM_FREE( toolitem->help ); IM_FREE( toolitem->action ); IM_FREE( toolitem->path ); IM_FREE( toolitem->user_path ); IM_FREE( toolitem ); return( NULL ); } static Toolitem * toolitem_new( Toolitem *parent, Compile *compile, Tool *tool ) { Toolitem *toolitem; if( !(toolitem = INEW( NULL, Toolitem )) ) return( NULL ); toolitem->compile = compile; toolitem->tool = tool; toolitem->action_sym = NULL; toolitem->is_separator = FALSE; toolitem->is_pullright = FALSE; toolitem->children = NULL; toolitem->parent = parent; toolitem->is_action = FALSE; toolitem->label = NULL; toolitem->name = NULL; toolitem->icon = NULL; toolitem->tooltip = NULL; toolitem->help = NULL; toolitem->action = NULL; toolitem->path = NULL; toolitem->user_path = NULL; if( parent ) parent->children = g_slist_append( parent->children, toolitem ); return( toolitem ); } /* Set label & name & icon. FIXME ... we will do repeated heap_is_instanceof() during item build, do it once and set a flag instead */ static void toolitem_set_name( Toolitem *toolitem, PElement *root ) { gboolean result; char value[MAX_NAME]; int i; if( root && heap_is_instanceof( CLASS_MENUITEM, root, &result ) && result ) { if( class_get_member_string( root, MEMBER_LABEL, value, MAX_NAME ) ) { char *p, *q; /* Save the i18n-ed version. */ IM_SETSTR( toolitem->label, _( value ) ); /* Strip underscores (they mark mnemonics). Can't use * strrcpy() or memccpy(), we have overlapping blocks. */ im_strncpy( value, toolitem->label, MAX_NAME ); for( p = q = value; *p; p++ ) if( *p != '_' ) *q++ = *p; *q = '\0'; IM_SETSTR( toolitem->name, value ); } if( class_get_member_string( root, MEMBER_ICON, value, MAX_NAME ) ) IM_SETSTR( toolitem->icon, value ); } else { /* Remove underscores from the object name ... we don't want * them to be mnemonics. */ im_strncpy( value, IOBJECT( toolitem->compile->sym )->name, MAX_NAME ); for( i = 0; value[i]; i++ ) if( value[i] == '_' ) value[i] = ' '; IM_SETSTR( toolitem->label, value ); IM_SETSTR( toolitem->name, toolitem->label ); } if( root && heap_is_instanceof( CLASS_MENUSEPARATOR, root, &result ) && result ) toolitem->is_separator = TRUE; } static void toolitem_set_tooltip( Toolitem *toolitem, PElement *root ) { gboolean result; char value[MAX_NAME]; if( root && heap_is_instanceof( CLASS_MENUITEM, root, &result ) && result && class_get_member_string( root, MEMBER_TOOLTIP, value, MAX_NAME ) ) { IM_SETSTR( toolitem->tooltip, _( value ) ); } else if( toolitem->tool && toolitem->tool->help ) IM_SETSTR( toolitem->tooltip, toolitem->tool->help ); } static void toolitem_set_pullright( Toolitem *toolitem, PElement *root ) { gboolean result; /* New-style pullright? */ if( root && heap_is_instanceof( CLASS_MENUPULLRIGHT, root, &result ) && result ) toolitem->is_pullright = TRUE; /* Old-style pullright? */ else if( is_value( toolitem->compile->sym ) && is_class( toolitem->compile ) && !toolitem->compile->has_super && toolitem->compile->nparam == 0 ) toolitem->is_pullright = TRUE; } static void toolitem_set_action( Toolitem *toolitem, PElement *root ) { gboolean result; char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( toolitem->parent ) vips_buf_appendf( &buf, "%s.", toolitem->parent->action ); vips_buf_appendf( &buf, "%s", IOBJECT( toolitem->compile->sym )->name ); /* If this is a Menuaction, we need the action member. */ if( root && heap_is_instanceof( CLASS_MENUACTION, root, &result ) && result ) { PElement out; toolitem->is_action = TRUE; (void) class_get_member( root, MEMBER_ACTION, &toolitem->action_sym, &out ); } /* If there's an action member, use that. */ if( toolitem->is_action ) vips_buf_appends( &buf, "." MEMBER_ACTION ); IM_SETSTR( toolitem->action, vips_buf_all( &buf ) ); /* No action member found and this is an item (ie. not a pullright)? * Default to the sym itself. */ if( !toolitem->action_sym && !toolitem->is_pullright ) toolitem->action_sym = toolitem->compile->sym; } static void toolitem_set_path( Toolitem *toolitem ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( toolitem->parent ) vips_buf_appendf( &buf, "%s", toolitem->parent->path ); else vips_buf_appendf( &buf, "/Toolkits/%s", IOBJECT( toolitem->tool->kit )->name ); vips_buf_appendf( &buf, "/%s", toolitem->name ); IM_SETSTR( toolitem->path, vips_buf_all( &buf ) ); } static void toolitem_set_user_path( Toolitem *toolitem ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( toolitem->parent ) vips_buf_appends( &buf, toolitem->parent->user_path ); else vips_buf_appends( &buf, IOBJECT( toolitem->tool->kit )->name ); vips_buf_appendf( &buf, " / %s", toolitem->name ); IM_SETSTR( toolitem->user_path, vips_buf_all( &buf ) ); } static void * toolitem_set_help_sub( Symbol *param, VipsBuf *buf ) { vips_buf_appends( buf, " " ); vips_buf_appends( buf, IOBJECT( param )->name ); return( NULL ); } static void toolitem_set_help( Toolitem *toolitem ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_appends( &buf, toolitem->name ); /* Get the params from the action member if we can. */ if( toolitem->action_sym && toolitem->action_sym->expr && toolitem->action_sym->expr->compile->param ) slist_map( toolitem->action_sym->expr->compile->param, (SListMapFn) toolitem_set_help_sub, &buf ); vips_buf_appends( &buf, ": " ); if( toolitem->tooltip ) vips_buf_appends( &buf, toolitem->tooltip ); IM_SETSTR( toolitem->help, vips_buf_firstline( &buf ) ); } static Toolitem * toolitem_build( Tool *tool, Compile *compile, PElement *root, Toolitem *parent ) { Toolitem *toolitem; if( !(toolitem = toolitem_new( parent, compile, tool )) ) return( NULL ); toolitem_set_name( toolitem, root ); toolitem_set_tooltip( toolitem, root ); toolitem_set_pullright( toolitem, root ); toolitem_set_action( toolitem, root ); toolitem_set_path( toolitem ); toolitem_set_user_path( toolitem ); toolitem_set_help( toolitem ); #ifdef DEBUG_TOOLITEM printf( "toolitem_build: %s\n", toolitem->name ); #endif /*DEBUG_TOOLITEM*/ #ifdef DEBUG_VERBOSE printf( "toolitem_build:\n" ); printf( "\tpullright = %d\n", toolitem->is_pullright ); printf( "\tlabel = \"%s\"\n", toolitem->label ); printf( "\tname = \"%s\"\n", toolitem->name ); printf( "\ticon = \"%s\"\n", toolitem->icon ); printf( "\ttooltip = \"%s\"\n", toolitem->tooltip ); printf( "\thelp = \"%s\"\n", toolitem->help ); printf( "\taction = \"%s\"\n", toolitem->action ); printf( "\tpath = \"%s\"\n", toolitem->path ); printf( "\tuser_path = \"%s\"\n", toolitem->user_path ); #endif /*DEBUG_VERBOSE*/ return( toolitem ); } static Toolitem * toolitem_build_all( Tool *tool, Compile *compile, PElement *root, Toolitem *parent ); static void * toolitem_build_all_sub( Symbol *sym, Toolitem *parent ) { if( is_menuable( sym ) ) (void) toolitem_build_all( parent->tool, sym->expr->compile, NULL, parent ); return( NULL ); } static Toolitem * toolitem_build_all( Tool *tool, Compile *compile, PElement *root, Toolitem *parent ) { Toolitem *toolitem; gboolean result; if( !(toolitem = toolitem_build( tool, compile, root, parent )) ) return( NULL ); /* If this is a dynamic pullright, walk the heap to find the members. */ if( toolitem->is_pullright && root && heap_is_instanceof( CLASS_MENUPULLRIGHT, root, &result ) && result ) { PElement member; HeapNode *p; PEGETCLASSMEMBER( &member, root ); if( PEISNODE( &member ) ) for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) { PElement s, v; HeapNode *hn; Symbol *sym; /* Get the sym/value pair. */ hn = GETLEFT( p ); PEPOINTLEFT( hn, &s ); PEPOINTRIGHT( hn, &v ); sym = SYMBOL( PEGETSYMREF( &s ) ); /* Ignore this/super/check etc. */ if( !is_menuable( sym ) ) continue; /* For dynamic menus, only make items for * things which are subclasses of menu. */ if( !heap_is_instanceof( CLASS_MENU, &v, &result ) || !result ) continue; (void) toolitem_build_all( tool, sym->expr->compile, &v, toolitem ); } } else if( toolitem->is_pullright ) { /* A static pullright... just walk the container. */ (void) icontainer_map( ICONTAINER( compile ), (icontainer_map_fn) toolitem_build_all_sub, toolitem, NULL ); } return( toolitem ); } #ifdef DEBUG_MENUS static void toolitem_print( Toolitem *toolitem ) { if( toolitem->is_separator ) printf( "-----------\n" ); else printf( "%s --- %s\n", NN( toolitem->user_path ), NN( toolitem->help ) ); } static void * toolitem_print_all( Toolitem *toolitem ) { if( toolitem->is_pullright ) slist_map( toolitem->children, (SListMapFn) toolitem_print_all, NULL ); else toolitem_print( toolitem ); return( NULL ); } #endif /*DEBUG_MENUS*/ /* Rebuild the toolitem tree. */ static void tool_toolitem_rebuild( Tool *tool ) { IM_FREEF( toolitem_free, tool->toolitem ); switch( tool->type ) { case TOOL_SYM: if( is_menuable( tool->sym ) ) tool->toolitem = toolitem_build_all( tool, tool->sym->expr->compile, &tool->sym->expr->root, NULL ); break; case TOOL_DIA: if( (tool->toolitem = toolitem_new( NULL, NULL, tool )) ) IM_SETSTR( tool->toolitem->label, IOBJECT( tool )->name ); break; case TOOL_SEP: if( (tool->toolitem = toolitem_new( NULL, NULL, tool )) ) tool->toolitem->is_separator = TRUE; break; default: g_assert( 0 ); } iobject_changed( IOBJECT( tool ) ); #ifdef DEBUG_MENUS if( tool->toolitem ) toolitem_print_all( tool->toolitem ); #endif /*DEBUG_MENUS*/ } /* The expr has a new value. */ static void tool_new_value_cb( Symbol *sym, Tool *tool ) { #ifdef DEBUG printf( "tool_new_value_cb: new value for " ); symbol_name_print( sym ); printf( "\n" ); #endif /*DEBUG*/ tool_toolitem_rebuild( tool ); } static void tool_set_help( Tool *tool ) { char *p; char value[MAX_NAME]; if( tool->sym && tool->sym->expr && tool->sym->expr->compile && (p = tool->sym->expr->compile->text) ) { /* Skip leading whitespace. */ while( isspace( (int)(*p) ) ) p++; /* Skip leading comment, if any. */ if( p[0] == '/' && p[1] == '*' ) p += 2; else if( p[0] == '/' && p[1] == '/' ) p += 2; /* Skip more whitespace. */ while( isspace( (int)(*p) ) ) p++; /* Limit to MAX_NAME chars or 1st line. Strip trailing * whitespace. */ im_strncpy( value, p, MAX_NAME ); if( (p = strchr( value, '\n' )) ) *p = '\0'; *((char *) my_strrspn( value, WHITESPACE )) = '\0'; IM_SETSTR( tool->help, value ); } else if( tool->sym && tool->sym->type == SYM_EXTERNAL ) IM_SETSTR( tool->help, tool->sym->function->desc ); else if( tool->sym && tool->sym->type == SYM_BUILTIN ) IM_SETSTR( tool->help, tool->sym->builtin->desc ); else IM_SETSTR( tool->help, NULL ); } /* Add a symbol to a toolkit. */ Tool * tool_new_sym( Toolkit *kit, int pos, Symbol *sym ) { Tool *tool; g_assert( kit && sym ); /* Is there a tool we can reuse? Don't update pos .. assume we want to * keep the old one. */ if( (tool = sym->tool) && tool->kit == kit ) { tool->lineno = -1; tool_set_help( tool ); return( tool ); } /* Junk any existing tool for this sym. */ if( (tool = sym->tool) ) { sym->tool = NULL; tool->sym = NULL; IDESTROY( tool ); } tool = TOOL( g_object_new( TYPE_TOOL, NULL ) ); tool->type = TOOL_SYM; tool->sym = sym; sym->tool = tool; tool->new_value_sid = g_signal_connect( sym, "new_value", G_CALLBACK( tool_new_value_cb ), tool ); tool->link_sym = sym; tool_link( tool, kit, pos, IOBJECT( sym )->name ); tool_set_help( tool ); #ifdef DEBUG printf( "tool_new_sym: new tool for " ); symbol_name_print( sym ); printf( "at %p\n", tool ); #endif /*DEBUG*/ return( tool ); } /* Add a separator to a toolkit. */ Tool * tool_new_sep( Toolkit *kit, int pos ) { Tool *tool; g_assert( kit ); tool = TOOL( g_object_new( TYPE_TOOL, NULL ) ); tool->type = TOOL_SEP; iobject_set( IOBJECT( tool ), "separator", NULL ); tool_link( tool, kit, pos, NULL ); tool_toolitem_rebuild( tool ); return( tool ); } /* Search a kit for a tool by tool name. Used for searching for dialogs ... we * can't use the symtable stuff, as they're not syms. */ static Tool * tool_find( Toolkit *kit, const char *name ) { return( (Tool *) icontainer_map( ICONTAINER( kit ), (icontainer_map_fn) iobject_test_name, (char *) name, NULL ) ); } /* Add a dialog entry to a toolkit. */ Tool * tool_new_dia( Toolkit *kit, int pos, const char *name, const char *filename ) { Tool *tool; g_assert( kit && name && filename ); if( (tool = tool_find( kit, name )) ) { if( tool->type != TOOL_DIA ) { error_top( _( "Name clash." ) ); error_sub( _( "Can't create dialog with name \"%s\", " "an object with that name already exists in " "kit \"%s\"." ), name, IOBJECT( kit )->name ); return( NULL ); } /* Just update the filename. */ filemodel_set_filename( FILEMODEL( tool ), filename ); tool->lineno = -1; } else { tool = TOOL( g_object_new( TYPE_TOOL, NULL ) ); tool->type = TOOL_DIA; filemodel_set_filename( FILEMODEL( tool ), filename ); iobject_set( IOBJECT( tool ), name, NULL ); tool_link( tool, kit, pos, NULL ); } tool_toolitem_rebuild( tool ); return( tool ); } static Toolitem * toolitem_lookup_toolitem( Toolitem *toolitem, Symbol *action ) { if( toolitem->action_sym == action ) return( toolitem ); else return( (Toolitem *) slist_map( toolitem->children, (SListMapFn) toolitem_lookup_toolitem, action ) ); } static Toolitem * toolitem_lookup_tool( Tool *tool, Symbol *action ) { if( tool->toolitem ) return( toolitem_lookup_toolitem( tool->toolitem, action ) ); else return( NULL ); } static Toolitem * toolitem_lookup_toolkit( Toolkit *kit, Symbol *action ) { return( (Toolitem *) toolkit_map( kit, (tool_map_fn) toolitem_lookup_tool, action, NULL ) ); } /* Just walk the whole kit. Could use a hash in kitg, but we don't call this * so often. */ Toolitem * toolitem_lookup( Toolkitgroup *kitg, Symbol *action ) { return( (Toolitem *) toolkitgroup_map( kitg, (toolkit_map_fn) toolitem_lookup_toolkit, action, NULL ) ); } ================================================ FILE: src/tool.h ================================================ /* Tools ... mostly a menu item. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Build a tree of these for each tool we make. */ struct _Toolitem { /* The thing for which we are making an item. Eg. if the .def file * has 'fred = class Menuitem "poop" "lots of poop" {}', this is the * Compile for fred. * * compile is not always valid .. eg. for #dialog or #separator */ Compile *compile; /* The top-level tool we come from. */ Tool *tool; /* The symbol we perform the action with (eg. get nparam from this). */ Symbol *action_sym; /* Set for a separator. */ gboolean is_separator; /* Set if we decide during build that this item should be a pullright. */ gboolean is_pullright; /* If this is a pullright, the children of this item. If we are a * child, the parent. */ GSList *children; Toolitem *parent; /* Set if we decide this should have an action. */ gboolean is_action; char *label; /* eg. "W_hite Balance" */ char *name; /* eg. "White Balance" */ char *icon; /* eg. "$VIPSHOME/icons/wb.png" */ char *tooltip; /* eg. "move whitepoint to region neutral" */ char *help; /* eg. "White Balance r: move ..." */ char *action; /* eg. "White_balance_widget._action" */ char *path; /* eg. "/Toolkits/Image" */ char *user_path; /* eg. "Image / White Balance" */ }; #define TYPE_TOOL (tool_get_type()) #define TOOL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_TOOL, Tool )) #define TOOL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_TOOL, ToolClass)) #define IS_TOOL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_TOOL )) #define IS_TOOL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_TOOL )) #define TOOL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_TOOL, ToolClass )) /* Tool types: a def (sym points to symbol for this def), a dialog (keep * filename and prompt name), or a separator. */ typedef enum { TOOL_SYM, TOOL_DIA, TOOL_SEP } Tooltype; /* What we hold for each tool. */ struct _Tool { Filemodel parent_class; Tooltype type; Symbol *sym; /* For SYM tools: symbol this tool represents */ guint new_value_sid; /* Watch for new_value with this */ Symbol *link_sym; /* the sym we are watching (in case ->sym is NULLed before we try to disconnect */ Toolkit *kit; /* Link back to toolkit */ int lineno; /* -1 for not known, or lineno in kit */ Toolitem *toolitem; /* Items made by this tool */ /* The first line of the comment prior to the definition. Toolitem help * and tooltip can be generated from the Menuitem members. */ char *help; /* eg. "concat l: join a list of .." */ }; typedef struct _ToolClass { FilemodelClass parent_class; /* My methods. */ } ToolClass; void tool_error( Tool *tool, VipsBuf *buf ); void *tool_linkreport_tool( Tool *tool, VipsBuf *buf, gboolean *found ); GType tool_get_type( void ); Tool *tool_new_sym( Toolkit *kit, int pos, Symbol *sym ); Tool *tool_new_sep( Toolkit *kit, int pos ); Tool *tool_new_dia( Toolkit *kit, int pos, const char *filename, const char *name ); Toolitem *toolitem_lookup( Toolkitgroup *kitg, Symbol *action ); ================================================ FILE: src/toolkit.c ================================================ /* Manage toolkits and their display. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static FilemodelClass *parent_class = NULL; Tool * toolkit_map( Toolkit *kit, tool_map_fn fn, void *a, void *b ) { return( (Tool *) icontainer_map( ICONTAINER( kit ), (icontainer_map_fn) fn, a, b ) ); } static void toolkit_changed( iObject *iobject ) { /* If we change, signal change on our parent too (toolkitgroup) ... * things like Program and Toolkitbrowser which need to spot any * change to any kit can connect to that, rather than having to * connect to all kits independently. */ if( IS_ICONTAINER( iobject ) && ICONTAINER( iobject )->parent ) iobject_changed( IOBJECT( ICONTAINER( iobject )->parent ) ); } static void toolkit_info( iObject *iobject, VipsBuf *buf ) { Toolkit *kit = TOOLKIT( iobject ); IOBJECT_CLASS( parent_class )->info( iobject, buf ); vips_buf_appendf( buf, "group = \"%s\"\n", IOBJECT( kit->kitg )->name ); } static View * toolkit_view_new( Model *model, View *parent ) { return( toolkitview_new() ); } static gboolean toolkit_save_text( Model *model, iOpenFile *of ) { if( icontainer_map( ICONTAINER( model ), (icontainer_map_fn) model_save_text, of, NULL ) ) return( FALSE ); return( TRUE ); } /* Load from an iOpenFile. */ static gboolean toolkit_load_text( Model *model, Model *parent, iOpenFile *of ) { Toolkit *kit = TOOLKIT( model ); int pos = icontainer_pos_last( ICONTAINER( model ) ) + 1; gboolean res; /* Load up definitions. */ filemodel_set_filename( FILEMODEL( model ), of->fname_real ); attach_input_file( of ); if( !(res = parse_toplevel( kit, pos )) ) /* The sub won't have filename or line number: zap them in. */ error_sub( "%s:%d\n%s\n", FILEMODEL( kit )->filename, input_state.lineno, error_get_sub() ); #ifdef DEBUG (void) dump_kit( kit ); #endif /*DEBUG*/ return( res ); } static void toolkit_class_init( ToolkitClass *class ) { iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; FilemodelClass *filemodel_class = (FilemodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ iobject_class->info = toolkit_info; iobject_class->changed = toolkit_changed; model_class->view_new = toolkit_view_new; model_class->save_text = toolkit_save_text; model_class->load_text = toolkit_load_text; filemodel_class->filetype = filesel_type_definition; } static void toolkit_init( Toolkit *kit ) { kit->kitg = NULL; kit->pseudo = FALSE; } GType toolkit_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ToolkitClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) toolkit_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Toolkit ), 32, /* n_preallocs */ (GInstanceInitFunc) toolkit_init, }; type = g_type_register_static( TYPE_FILEMODEL, "Toolkit", &info, 0 ); } return( type ); } static void toolkit_link( Toolkit *kit, Toolkitgroup *kitg, const char *name ) { iobject_set( IOBJECT( kit ), name, NULL ); icontainer_child_add( ICONTAINER( kitg ), ICONTAINER( kit ), -1 ); kit->kitg = kitg; filemodel_register( FILEMODEL( kit ) ); if( name[0] == '_' ) MODEL( kit )->display = FALSE; toolkitgroup_sort( kitg ); } /* Find a kit by kit name. */ Toolkit * toolkit_find( Toolkitgroup *kitg, const char *name ) { return( (Toolkit *) icontainer_map( ICONTAINER( kitg ), (icontainer_map_fn) iobject_test_name, (char *) name, NULL ) ); } Toolkit * toolkit_new( Toolkitgroup *kitg, const char *name ) { Toolkit *kit; #ifdef DEBUG printf( "toolkit_new: %s\n", name ); #endif /*DEBUG*/ /* Exists already? */ if( (kit = toolkit_find( kitg, name )) ) IDESTROY( kit ); /* Make a new kit. */ kit = TOOLKIT( g_object_new( TYPE_TOOLKIT, NULL ) ); toolkit_link( kit, kitg, name ); return( kit ); } Toolkit * toolkit_new_filename( Toolkitgroup *kitg, const char *filename ) { char name[FILENAME_MAX]; Toolkit *kit; name_from_filename( filename, name ); kit = toolkit_new( kitg, name ); filemodel_set_filename( FILEMODEL( kit ), filename ); return( kit ); } /* Load a file as a toolkit. */ Toolkit * toolkit_new_from_file( Toolkitgroup *kitg, const char *filename ) { Toolkit *kit = toolkit_new_filename( kitg, filename ); gboolean res; res = filemodel_load_all( FILEMODEL( kit ), MODEL( kitg ), filename, NULL ); filemodel_set_modified( FILEMODEL( kit ), FALSE ); /* Don't remove the kit if load failed, we want to leave it so the * user can try to fix the problem. */ if( res ) return( kit ); else return( NULL ); } /* Load from an iOpenFile. */ Toolkit * toolkit_new_from_openfile( Toolkitgroup *kitg, iOpenFile *of ) { Toolkit *kit = toolkit_new_filename( kitg, of->fname ); gboolean res; res = filemodel_load_all_openfile( FILEMODEL( kit ), MODEL( kitg ), of ); filemodel_set_modified( FILEMODEL( kit ), FALSE ); /* Don't remove the kit if load failed, we want to leave it so the * user can try to fix the problem. */ if( res ) return( kit ); else return( NULL ); } /* Look up a toolkit, make an empty one if not there. */ Toolkit * toolkit_by_name( Toolkitgroup *kitg, const char *name ) { Toolkit *kit; if( !(kit = toolkit_find( kitg, name )) ) { char file[FILENAME_MAX]; im_snprintf( file, FILENAME_MAX, "$SAVEDIR" G_DIR_SEPARATOR_S "start" G_DIR_SEPARATOR_S "%s.def", name ); kit = toolkit_new_filename( kitg, file ); } return( kit ); } ================================================ FILE: src/toolkit.h ================================================ /* Groups of tools! */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOOLKIT (toolkit_get_type()) #define TOOLKIT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_TOOLKIT, Toolkit )) #define TOOLKIT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_TOOLKIT, ToolkitClass)) #define IS_TOOLKIT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_TOOLKIT )) #define IS_TOOLKIT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_TOOLKIT )) #define TOOLKIT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_TOOLKIT, ToolkitClass )) /* Toolkits: group definitions with these guys. One toolkit per definition file * loaded. */ struct _Toolkit { Filemodel parent_class; Toolkitgroup *kitg; /* Set this for auto-generated toolkits (eg. packages of function from * VIPS) ... blocks edit etc. in program window. */ gboolean pseudo; }; typedef struct _ToolkitClass { FilemodelClass parent_class; /* My methods. */ } ToolkitClass; Tool *toolkit_map( Toolkit *kit, tool_map_fn fn, void *a, void *b ); GType toolkit_get_type( void ); Toolkit *toolkit_find( Toolkitgroup *kitg, const char *name ); Toolkit *toolkit_by_name( Toolkitgroup *kitg, const char *name ); Toolkit *toolkit_new( Toolkitgroup *kitg, const char *filename ); Toolkit *toolkit_new_filename( Toolkitgroup *kitg, const char *filename ); Toolkit *toolkit_new_from_file( Toolkitgroup *kitg, const char *filename ); Toolkit *toolkit_new_from_openfile( Toolkitgroup *kitg, iOpenFile *of ); void *toolkit_linkreport( Toolkit *kit, VipsBuf *buf, gboolean *bad_links ); ================================================ FILE: src/toolkitbrowser.c ================================================ /* Toolkitbrowser dialog. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; /* Our columns. */ enum { TOOLTIP_COLUMN, /* Visible columns */ MENU_COLUMN, NPARAM_COLUMN, TOOLITEM_COLUMN, /* Secret column */ N_COLUMNS }; static void toolkitbrowser_destroy( GtkObject *object ) { Toolkitbrowser *toolkitbrowser = TOOLKITBROWSER( object ); UNREF( toolkitbrowser->filter ); UNREF( toolkitbrowser->store ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void * toolkitbrowser_rebuild_item_sub( Symbol *param, VipsBuf *buf ) { vips_buf_appends( buf, " " ); vips_buf_appends( buf, IOBJECT( param )->name ); return( NULL ); } static void * toolkitbrowser_rebuild_item3( Toolitem *toolitem, Toolkitbrowser *toolkitbrowser ) { if( !toolitem->is_pullright && !toolitem->is_separator && toolitem->compile ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); GtkTreeIter iter; if( toolitem->action_sym && toolitem->action_sym->expr && toolitem->action_sym->expr->compile->param ) slist_map( toolitem->action_sym->expr->compile->param, (SListMapFn) toolkitbrowser_rebuild_item_sub, &buf ); gtk_list_store_append( toolkitbrowser->store, &iter ); gtk_list_store_set( toolkitbrowser->store, &iter, TOOLTIP_COLUMN, toolitem->tooltip, MENU_COLUMN, toolitem->user_path, NPARAM_COLUMN, vips_buf_all( &buf ), TOOLITEM_COLUMN, toolitem, -1 ); } slist_map( toolitem->children, (SListMapFn) toolkitbrowser_rebuild_item3, toolkitbrowser ); return( NULL ); } static void * toolkitbrowser_rebuild_item2( Tool *tool, Toolkitbrowser *toolkitbrowser ) { if( tool->toolitem ) toolkitbrowser_rebuild_item3( tool->toolitem, toolkitbrowser ); return( NULL ); } static void * toolkitbrowser_rebuild_item( Toolkit *kit, Toolkitbrowser *toolkitbrowser ) { toolkit_map( kit, (tool_map_fn) toolkitbrowser_rebuild_item2, toolkitbrowser, NULL ); return( NULL ); } static void toolkitbrowser_refresh( vObject *vobject ) { Toolkitbrowser *toolkitbrowser = TOOLKITBROWSER( vobject ); #ifdef DEBUG printf( "toolkitbrowser_refresh:\n" ); #endif /*DEBUG*/ gtk_list_store_clear( toolkitbrowser->store ); toolkitgroup_map( toolkitbrowser->kitg, (toolkit_map_fn) toolkitbrowser_rebuild_item, toolkitbrowser, NULL ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void toolkitbrowser_link( vObject *vobject, iObject *iobject ) { Toolkitbrowser *toolkitbrowser = TOOLKITBROWSER( vobject ); Toolkitgroup *kitg = TOOLKITGROUP( iobject ); g_assert( !toolkitbrowser->kitg ); toolkitbrowser->kitg = kitg; VOBJECT_CLASS( parent_class )->link( vobject, iobject ); } static void toolkitbrowser_class_init( ToolkitbrowserClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = toolkitbrowser_destroy; vobject_class->refresh = toolkitbrowser_refresh; vobject_class->link = toolkitbrowser_link; } static void toolkitbrowser_entry_changed_cb( GtkEditable *editable, Toolkitbrowser *toolkitbrowser ) { gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( toolkitbrowser->filter ) ); } static gboolean toolkitbrowser_rebuild_test( Toolitem *toolitem, const char *text ) { if( my_strcasestr( toolitem->user_path, text ) || my_strcasestr( toolitem->tooltip, text ) ) return( TRUE ); return( FALSE ); } static gboolean toolkitbrowser_visible_func( GtkTreeModel *model, GtkTreeIter *iter, gpointer data ) { Toolkitbrowser *toolkitbrowser = TOOLKITBROWSER( data ); const char *text = gtk_entry_get_text( GTK_ENTRY( toolkitbrowser->entry ) ); Toolitem *toolitem; gtk_tree_model_get( model, iter, TOOLITEM_COLUMN, &toolitem, -1 ); if( !toolitem ) return( FALSE ); return( toolkitbrowser_rebuild_test( toolitem, text ) ); } static Toolitem * toolkitbrowser_get_selected( Toolkitbrowser *toolkitbrowser ) { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( toolkitbrowser->tree ) ); GtkTreeIter iter; GtkTreeModel *model; Toolitem *toolitem; if( gtk_tree_selection_get_selected( selection, &model, &iter ) ) { gtk_tree_model_get( model, &iter, TOOLITEM_COLUMN, &toolitem, -1 ); return( toolitem ); } return( NULL ); } static gboolean toolkitbrowser_activate_selected( Toolkitbrowser *toolkitbrowser ) { Toolitem *toolitem; if( (toolitem = toolkitbrowser_get_selected( toolkitbrowser )) ) { if( !workspace_add_action( toolkitbrowser->ws, toolitem->name, toolitem->action, toolitem->action_sym->expr->compile->nparam ) ) return( FALSE ); } return( TRUE ); } static void toolkitbrowser_row_activated_cb( GtkTreeView *treeview, GtkTreePath *arg1, GtkTreeViewColumn *arg2, Toolkitbrowser *toolkitbrowser ) { if( !toolkitbrowser_activate_selected( toolkitbrowser ) ) iwindow_alert( GTK_WIDGET( toolkitbrowser ), GTK_MESSAGE_ERROR ); } static void toolkitbrowser_init( Toolkitbrowser *toolkitbrowser ) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *label; GtkWidget *swin; toolkitbrowser->top = gtk_hbox_new( FALSE, 12 ); toolkitbrowser->entry = gtk_entry_new(); gtk_signal_connect( GTK_OBJECT( toolkitbrowser->entry ), "changed", GTK_SIGNAL_FUNC( toolkitbrowser_entry_changed_cb ), toolkitbrowser ); gtk_box_pack_end( GTK_BOX( toolkitbrowser->top ), toolkitbrowser->entry, FALSE, FALSE, 2 ); label = gtk_image_new_from_stock( GTK_STOCK_FIND, GTK_ICON_SIZE_MENU ); gtk_box_pack_end( GTK_BOX( toolkitbrowser->top ), label, FALSE, FALSE, 0 ); gtk_box_pack_start( GTK_BOX( toolkitbrowser ), toolkitbrowser->top, FALSE, FALSE, 2 ); gtk_widget_show_all( toolkitbrowser->top ); toolkitbrowser->store = gtk_list_store_new( N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER ); toolkitbrowser->filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( toolkitbrowser->store ), NULL ); gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER( toolkitbrowser->filter ), toolkitbrowser_visible_func, toolkitbrowser, NULL ); toolkitbrowser->tree = gtk_tree_view_new_with_model( GTK_TREE_MODEL( toolkitbrowser->filter ) ); gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( toolkitbrowser->tree ), TRUE ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Action" ), renderer, "text", TOOLTIP_COLUMN, NULL ); gtk_tree_view_column_set_resizable( column, TRUE ); gtk_tree_view_column_set_reorderable( column, TRUE ); gtk_tree_view_append_column( GTK_TREE_VIEW( toolkitbrowser->tree ), column ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Parameters" ), renderer, "text", NPARAM_COLUMN, NULL ); gtk_tree_view_column_set_resizable( column, TRUE ); gtk_tree_view_column_set_reorderable( column, TRUE ); gtk_tree_view_append_column( GTK_TREE_VIEW( toolkitbrowser->tree ), column ); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _( "Menu Item" ), renderer, "text", MENU_COLUMN, NULL ); gtk_tree_view_column_set_resizable( column, TRUE ); gtk_tree_view_column_set_reorderable( column, TRUE ); gtk_tree_view_append_column( GTK_TREE_VIEW( toolkitbrowser->tree ), column ); g_signal_connect( G_OBJECT( toolkitbrowser->tree ), "row-activated", G_CALLBACK( toolkitbrowser_row_activated_cb ), toolkitbrowser ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_container_add( GTK_CONTAINER( swin ), toolkitbrowser->tree ); gtk_box_pack_start( GTK_BOX( toolkitbrowser ), swin, TRUE, TRUE, 2 ); gtk_widget_show_all( swin ); } GtkType toolkitbrowser_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Toolkitbrowser", sizeof( Toolkitbrowser ), sizeof( ToolkitbrowserClass ), (GtkClassInitFunc) toolkitbrowser_class_init, (GtkObjectInitFunc) toolkitbrowser_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VOBJECT, &info ); } return( type ); } Toolkitbrowser * toolkitbrowser_new( void ) { Toolkitbrowser *toolkitbrowser = gtk_type_new( TYPE_TOOLKITBROWSER ); return( toolkitbrowser ); } /* Find the 'natural' width of the browser. */ int toolkitbrowser_get_width( Toolkitbrowser *toolkitbrowser ) { if( toolkitbrowser->top ) return( toolkitbrowser->top->requisition.width ); else return( 200 ); } void toolkitbrowser_set_workspace( Toolkitbrowser *toolkitbrowser, Workspace *ws ) { g_assert( !toolkitbrowser->ws ); toolkitbrowser->ws = ws; } ================================================ FILE: src/toolkitbrowser.h ================================================ /* Toolkit browser */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOOLKITBROWSER (toolkitbrowser_get_type()) #define TOOLKITBROWSER( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_TOOLKITBROWSER, Toolkitbrowser )) #define TOOLKITBROWSER_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_TOOLKITBROWSER, ToolkitbrowserClass )) #define IS_TOOLKITBROWSER( obj ) \ (GTK_CHECK_TYPE( (obj), TYPE_TOOLKITBROWSER )) #define IS_TOOLKITBROWSER_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_TOOLKITBROWSER )) struct _Toolkitbrowser { vObject parent_object; Toolkitgroup *kitg; Workspace *ws; GtkListStore *store; /* Model for list view */ GtkTreeModel *filter; /* After filtering with search box */ GtkWidget *tree; /* Displayed tree */ GtkWidget *entry; /* Search widget */ GtkWidget *top; /* hbox for top bar */ }; typedef struct _ToolkitbrowserClass { vObjectClass parent_class; } ToolkitbrowserClass; GtkType toolkitbrowser_get_type( void ); void toolkitbrowser_set_mainw( Toolkitbrowser *toolkitbrowser, Mainw *mainw ); Toolkitbrowser *toolkitbrowser_new( void ); int toolkitbrowser_get_width( Toolkitbrowser *toolkitbrowser ); void toolkitbrowser_set_workspace( Toolkitbrowser *toolkitbrowser, Workspace *ws ); ================================================ FILE: src/toolkitgroup.c ================================================ /* Group toolkitgroup files together. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ModelClass *parent_class = NULL; Toolkit * toolkitgroup_map( Toolkitgroup *kitg, toolkit_map_fn fn, void *a, void *b ) { return( (Toolkit *) icontainer_map( ICONTAINER( kitg ), (icontainer_map_fn) fn, a, b ) ); } static void toolkitgroup_changed( iObject *iobject ) { #ifdef DEBUG g_print( "toolkitgroup_changed: " ); iobject_print( iobject ); #endif /*DEBUG*/ IOBJECT_CLASS( parent_class )->changed( iobject ); } static View * toolkitgroup_view_new( Model *model, View *parent ) { return( toolkitgroupview_new() ); } static void toolkitgroup_class_init( ToolkitgroupClass *class ) { iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ iobject_class->changed = toolkitgroup_changed; model_class->view_new = toolkitgroup_view_new; } static void toolkitgroup_init( Toolkitgroup *kitg ) { } GType toolkitgroup_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ToolkitgroupClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) toolkitgroup_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Toolkitgroup ), 32, /* n_preallocs */ (GInstanceInitFunc) toolkitgroup_init, }; type = g_type_register_static( TYPE_MODEL, "Toolkitgroup", &info, 0 ); } return( type ); } static void toolkitgroup_link( Toolkitgroup *kitg, Symbol *root ) { char buf[256]; g_assert( root ); kitg->root = root; im_snprintf( buf, 256, _( "Toolkits for %s" ), IOBJECT( root )->name ); iobject_set( IOBJECT( kitg ), buf, NULL ); } Toolkitgroup * toolkitgroup_new( Symbol *root ) { Toolkitgroup *kitg; kitg = TOOLKITGROUP( g_object_new( TYPE_TOOLKITGROUP, NULL ) ); toolkitgroup_link( kitg, root ); return( kitg ); } /* Need a special sort function ... put kits not being displayed at the end so * they don't mess up the numbering of the visible kits. */ static gint toolkitgroup_sort_compare( Model *a, Model *b ) { if( !a->display && b->display ) return( 1 ); if( a->display && !b->display ) return( -1 ); return( strcasecmp( IOBJECT( a )->name, IOBJECT( b )->name ) ); } void toolkitgroup_sort( Toolkitgroup *kitg ) { iContainer *icontainer = ICONTAINER( kitg ); icontainer->children = g_slist_sort( icontainer->children, (GCompareFunc) toolkitgroup_sort_compare ); icontainer_pos_renumber( icontainer ); } ================================================ FILE: src/toolkitgroup.h ================================================ /* Declarations for toolkitgroup.c */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOOLKITGROUP (toolkitgroup_get_type()) #define TOOLKITGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_TOOLKITGROUP, Toolkitgroup )) #define TOOLKITGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_TOOLKITGROUP, \ ToolkitgroupClass)) #define IS_TOOLKITGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_TOOLKITGROUP )) #define IS_TOOLKITGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_TOOLKITGROUP )) #define TOOLKITGROUP_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_TOOLKITGROUP, \ ToolkitgroupClass )) /* A toolkitgroup. */ struct _Toolkitgroup { Model parent_class; /* Defs in toolkits in this group are created as locals of this * symbol. This is symbol_root for the main toolkitgroup, but can be * local to a workspace if we are loading a set of compatibility defs. */ Symbol *root; }; typedef struct _ToolkitgroupClass { ModelClass parent_class; /* Methods. */ } ToolkitgroupClass; Toolkit *toolkitgroup_map( Toolkitgroup *kitg, toolkit_map_fn fn, void *a, void *b ); GType toolkitgroup_get_type( void ); Toolkitgroup *toolkitgroup_new( Symbol *root ); void toolkitgroup_sort( Toolkitgroup *kitg ); ================================================ FILE: src/toolkitgroupview.c ================================================ /* a toolkitgroupview button in a toolkitgroup */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static void * toolkitgroupview_dispose_sub( View *view, void *a, void *b ) { DESTROY_GTK( view ); return( NULL ); } static void toolkitgroupview_dispose( GObject *gobject ) { #ifdef DEBUG printf( "toolkitgroupview_dispose: %p\n", gobject ); #endif /*DEBUG*/ /* Toolkitviews are not child widgets of us, they are menu items pased * into the TK. Destroy them explicitly. */ view_map( VIEW( gobject ), toolkitgroupview_dispose_sub, NULL, NULL ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void toolkitgroupview_refresh( vObject *vobject ) { /* Toolkitgroupview *kitgview = TOOLKITGROUPVIEW( view ); */ /* FIXME ... should update display for reordering of toolkits (to keep * menu sorted) */ #ifdef DEBUG printf( "toolkitgroup changed\n" ); #endif /*DEBUG*/ VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void toolkitgroupview_class_init( ToolkitgroupviewClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->dispose = toolkitgroupview_dispose; /* Create signals. */ /* Set methods. */ vobject_class->refresh = toolkitgroupview_refresh; } static void toolkitgroupview_init( Toolkitgroupview *kitgview ) { } GtkType toolkitgroupview_get_type( void ) { static GtkType toolkitgroupview_type = 0; if( !toolkitgroupview_type ) { static const GtkTypeInfo info = { "Toolkitgroupview", sizeof( Toolkitgroupview ), sizeof( ToolkitgroupviewClass ), (GtkClassInitFunc) toolkitgroupview_class_init, (GtkObjectInitFunc) toolkitgroupview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; toolkitgroupview_type = gtk_type_unique( TYPE_VIEW, &info ); } return( toolkitgroupview_type ); } View * toolkitgroupview_new( void ) { Toolkitgroupview *kitgview = gtk_type_new( TYPE_TOOLKITGROUPVIEW ); return( VIEW( kitgview ) ); } void toolkitgroupview_set_mainw( Toolkitgroupview *kitgview, Mainw *mainw ) { kitgview->mainw = mainw; kitgview->menu = mainw->toolkit_menu; } ================================================ FILE: src/toolkitgroupview.h ================================================ /* a view of a toolkitgroup */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOOLKITGROUPVIEW (toolkitgroupview_get_type()) #define TOOLKITGROUPVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_TOOLKITGROUPVIEW, Toolkitgroupview )) #define TOOLKITGROUPVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_TOOLKITGROUPVIEW, \ ToolkitgroupviewClass )) #define IS_TOOLKITGROUPVIEW( obj ) \ (GTK_CHECK_TYPE( (obj), TYPE_TOOLKITGROUPVIEW )) #define IS_TOOLKITGROUPVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_TOOLKITGROUPVIEW )) struct _Toolkitgroupview { View parent_class; GtkWidget *menu; /* Display the toolkits in this */ Mainw *mainw; /* Mainw these menu items act on */ }; typedef struct _ToolkitgroupviewClass { ViewClass parent_class; /* My methods. */ } ToolkitgroupviewClass; GtkType toolkitgroupview_get_type( void ); View *toolkitgroupview_new( void ); void toolkitgroupview_set_mainw( Toolkitgroupview *kitgview, Mainw *mainw ); ================================================ FILE: src/toolkitview.c ================================================ /* Manage toolkitviews and their display. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* The top n items in the toolkits menu are made by the system for us ... we * pop toolkit items in after these. */ #define TOOLKITVIEW_MENU_OFFSET 3 static ViewClass *parent_class = NULL; static void toolkitview_destroy( GtkObject *object ) { Toolkitview *kview; g_return_if_fail( object != NULL ); g_return_if_fail( IS_TOOLKITVIEW( object ) ); kview = TOOLKITVIEW( object ); #ifdef DEBUG printf( "toolkitview_destroy: %p\n", object ); printf( "toolkitview_destroy: menu = %p\n", kview->menu ); printf( "toolkitview_destroy: item = %p\n", kview->item ); #endif /*DEBUG*/ DESTROY_GTK( kview->menu ); DESTROY_GTK( kview->item ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void toolkitview_finalize( GObject *gobject ) { #ifdef DEBUG printf( "toolkitview_finalize: %p\n", gobject ); #endif /*DEBUG*/ G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Our widgets have been killed ... kill us in turn. */ static void toolkitview_destroy_cb( GtkWidget *widget, Toolkitview *kview ) { /* printf( "toolkitview_destroy_cb: %p\n", kview ); */ kview->menu = NULL; kview->item = NULL; kview->destroy_sid = 0; DESTROY_GTK( kview ); } static void toolkitview_refresh( vObject *vobject ) { Toolkitview *kview = TOOLKITVIEW( vobject ); Toolkit *kit = TOOLKIT( VOBJECT( kview )->iobject ); Toolkitgroupview *kitgview = kview->kitgview; GtkWidget *menu = kitgview->menu; gboolean changed = FALSE; #ifdef DEBUG printf( "toolkitview_refresh: " ); iobject_print( VOBJECT( kview )->iobject ); #endif /*DEBUG*/ /* Make a button ready for the sub-menu. */ if( !kview->item ) { kview->item = gtk_menu_item_new_with_label( IOBJECT( kit )->name ); gtk_menu_shell_insert( GTK_MENU_SHELL( menu ), kview->item, ICONTAINER( kit )->pos + TOOLKITVIEW_MENU_OFFSET ); gtk_widget_show( kview->item ); kview->destroy_sid = g_signal_connect( kview->item, "destroy", G_CALLBACK( toolkitview_destroy_cb ), kview ); changed = TRUE; } if( !kview->menu ) { iWindow *iwnd = IWINDOW( iwindow_get_root( menu ) ); char path[256]; kview->menu = gtk_menu_new(); gtk_menu_set_accel_group( GTK_MENU( kview->menu ), iwnd->accel_group ); im_snprintf( path, 256, "/Toolkits/%s", IOBJECT( kit )->name ); gtk_menu_set_accel_path( GTK_MENU( kview->menu ), path ); changed = TRUE; } if( changed ) gtk_menu_item_set_submenu( GTK_MENU_ITEM( kview->item ), kview->menu ); widget_visible( kview->item, ICONTAINER( kit )->children != NULL ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void toolkitview_link( View *view, Model *model, View *parent ) { Toolkitview *kview = TOOLKITVIEW( view ); Toolkitgroupview *kitgview = TOOLKITGROUPVIEW( parent ); kview->kitgview = kitgview; VIEW_CLASS( parent_class )->link( view, model, parent ); #ifdef DEBUG printf( "toolkitview_link: " ); iobject_print( VOBJECT( kview )->iobject ); #endif /*DEBUG*/ } static void toolkitview_class_init( ToolkitviewClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; GtkObjectClass *object_class = (GtkObjectClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = toolkitview_finalize; object_class->destroy = toolkitview_destroy; /* Create signals. */ /* Init methods. */ vobject_class->refresh = toolkitview_refresh; view_class->link = toolkitview_link; } static void toolkitview_init( Toolkitview *kview ) { kview->item = NULL; kview->menu = NULL; kview->destroy_sid = 0; } GtkType toolkitview_get_type( void ) { static GtkType kview_type = 0; if( !kview_type ) { static const GtkTypeInfo kview_info = { "Toolkitview", sizeof( Toolkitview ), sizeof( ToolkitviewClass ), (GtkClassInitFunc) toolkitview_class_init, (GtkObjectInitFunc) toolkitview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; kview_type = gtk_type_unique( TYPE_VIEW, &kview_info ); } return( kview_type ); } View * toolkitview_new( void ) { Toolkitview *kview = gtk_type_new( TYPE_TOOLKITVIEW ); return( VIEW( kview ) ); } ================================================ FILE: src/toolkitview.h ================================================ /* View for toolkit. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOOLKITVIEW (toolkitview_get_type()) #define TOOLKITVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_TOOLKITVIEW, Toolkitview )) #define TOOLKITVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_TOOLKITVIEW, ToolkitviewClass )) #define IS_TOOLKITVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_TOOLKITVIEW )) #define IS_TOOLKITVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_TOOLKITVIEW )) struct _Toolkitview { View parent_class; Toolkitgroupview *kitgview; GtkWidget *menu; /* Menu for this kit */ GtkWidget *item; /* Menu item in enclosing menu */ guint destroy_sid; }; typedef struct _ToolkitviewClass { ViewClass parent_class; /* My methods. */ } ToolkitviewClass; GtkType toolkitview_get_type( void ); View *toolkitview_new( void ); ================================================ FILE: src/toolview.c ================================================ /* Manage toolviewkits and their display. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; /* Link menu items to toolview with this. */ static GQuark toolview_quark = 0; static Mainw * toolview_get_mainw( Toolview *tview ) { if( !tview->kview || !tview->kview->kitgview ) return( NULL ); return( tview->kview->kitgview->mainw ); } static Workspace * toolview_get_workspace( Toolview *tview ) { Mainw *mainw; if( !(mainw = toolview_get_mainw( tview )) ) return( NULL ); return( mainw_get_workspace( mainw ) ); } static Workspace * item_get_workspace( GtkWidget *item ) { Toolview *tview; if( !(tview = gtk_object_get_data_by_id( GTK_OBJECT( item ), toolview_quark )) ) return( NULL ); return( toolview_get_workspace( tview ) ); } static void toolview_destroy( GtkObject *object ) { Toolview *tview; g_return_if_fail( object != NULL ); g_return_if_fail( IS_TOOLVIEW( object ) ); tview = TOOLVIEW( object ); #ifdef DEBUG printf( "toolview_destroy: %p\n", object ); #endif /*DEBUG*/ DESTROY_GTK( tview->item ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void toolview_finalize( GObject *gobject ) { #ifdef DEBUG printf( "toolview_finalize: %p\n", gobject ); #endif /*DEBUG*/ G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void toolview_activate_cb( GtkWidget *widget, Toolitem *toolitem ) { Workspace *ws = item_get_workspace( widget ); switch( toolitem->tool->type ) { case TOOL_DIA: if( !workspace_merge_file( ws, FILEMODEL( toolitem->tool )->filename ) ) iwindow_alert( widget, GTK_MESSAGE_ERROR ); symbol_recalculate_all(); break; case TOOL_SYM: if( !workspace_add_action( ws, toolitem->name, toolitem->action, toolitem->action_sym->expr->compile->nparam ) ) iwindow_alert( widget, GTK_MESSAGE_ERROR ); break; default: g_assert( FALSE ); } } /* Flash help for a toolview. */ static void toolview_select_cb( GtkWidget *widget, Toolitem *toolitem ) { Workspace *ws = item_get_workspace( widget ); if( ws && toolitem->help ) workspace_set_status( ws, "%s", toolitem->help ); } /* Sub fn of below ... build a menu item for a TOOL_SYM. */ static GtkWidget * toolview_refresh_sub( Toolview *tview, Toolitem *toolitem, Workspace *ws, GtkWidget *menu ) { Mainw *mainw = toolview_get_mainw( tview ); GtkWidget *item; if( toolitem->is_separator ) item = gtk_menu_item_new(); else { item = gtk_image_menu_item_new_with_mnemonic( toolitem->label ); gtk_object_set_data_by_id( GTK_OBJECT( item ), toolview_quark, tview ); if( toolitem->icon ) gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( item ), image_new_from_file( toolitem->icon ) ); if( !toolitem->is_pullright ) gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( toolview_activate_cb ), toolitem ); if( toolitem->help && !toolitem->is_pullright ) set_tooltip( item, "%s", toolitem->help ); gtk_signal_connect( GTK_OBJECT( item ), "select", GTK_SIGNAL_FUNC( toolview_select_cb ), toolitem ); /* Make a pullright and recurse if necessary */ if( toolitem->is_pullright ) { GtkWidget *submenu = gtk_menu_new(); GSList *p; gtk_menu_set_accel_group( GTK_MENU( submenu ), IWINDOW( mainw )->accel_group ); gtk_menu_set_accel_path( GTK_MENU( submenu ), toolitem->path ); for( p = toolitem->children; p; p = p->next ) { Toolitem *child = p->data; toolview_refresh_sub( tview, child, ws, submenu ); } gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), submenu ); } } /* Is a top-level toolitem? */ if( toolitem == toolitem->tool->toolitem ) gtk_menu_shell_insert( GTK_MENU_SHELL( menu ), item, ICONTAINER( toolitem->tool )->pos + 1 ); else /* Submenus are always completely rebuilt, so we can just * append. */ gtk_menu_shell_append( GTK_MENU_SHELL( menu ), item ); gtk_widget_show( item ); return( item ); } /* Our widget has been destroyed. NULL out or pointer to it, to stop us * destroying it again later. */ void toolview_destroy_cb( GtkWidget *widget, Toolview *tview ) { g_assert( tview->item == widget ); tview->item = NULL; } /* Update toolview display. */ static void toolview_refresh( vObject *vobject ) { Toolview *tview = TOOLVIEW( vobject ); Workspace *ws = toolview_get_workspace( tview ); Tool *tool = TOOL( VOBJECT( tview )->iobject ); Toolkitview *kview = tview->kview; #ifdef DEBUG printf( "toolview_refresh: " ); iobject_print( VOBJECT( tview )->iobject ); #endif /*DEBUG*/ if( !toolview_quark ) toolview_quark = g_quark_from_static_string( "toolview_quark" ); DESTROY_GTK( tview->item ); if( tool->toolitem ) tview->item = toolview_refresh_sub( tview, tool->toolitem, ws, kview->menu ); if( tview->item ) gtk_signal_connect( GTK_OBJECT( tview->item ), "destroy", GTK_SIGNAL_FUNC( toolview_destroy_cb ), tview ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void toolview_link( View *view, Model *model, View *parent ) { Toolview *tview = TOOLVIEW( view ); Toolkitview *kview = TOOLKITVIEW( parent ); VIEW_CLASS( parent_class )->link( view, model, parent ); #ifdef DEBUG printf( "toolview_link: " ); iobject_print( VOBJECT( tview )->iobject ); #endif /*DEBUG*/ tview->kview = kview; } static void toolview_class_init( ToolviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass*) class; GObjectClass *gobject_class = (GObjectClass*) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = toolview_destroy; gobject_class->finalize = toolview_finalize; /* Create signals. */ /* Init methods. */ vobject_class->refresh = toolview_refresh; view_class->link = toolview_link; } static void toolview_init( Toolview *toolview ) { toolview->item = NULL; } GtkType toolview_get_type( void ) { static GtkType toolview_type = 0; if( !toolview_type ) { static const GtkTypeInfo toolview_info = { "Toolview", sizeof( Toolview ), sizeof( ToolviewClass ), (GtkClassInitFunc) toolview_class_init, (GtkObjectInitFunc) toolview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; toolview_type = gtk_type_unique( TYPE_VIEW, &toolview_info ); } return( toolview_type ); } View * toolview_new( void ) { Toolview *tview = gtk_type_new( TYPE_TOOLVIEW ); #ifdef DEBUG printf( "toolview_new: %p\n", tview ); #endif /*DEBUG*/ return( VIEW( tview ) ); } ================================================ FILE: src/toolview.h ================================================ /* View a tool as a menu item. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TOOLVIEW (toolview_get_type()) #define TOOLVIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_TOOLVIEW, Toolview )) #define TOOLVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_TOOLVIEW, ToolviewClass )) #define IS_TOOLVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_TOOLVIEW )) #define IS_TOOLVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_TOOLVIEW )) /* One of these for each top-level menu. */ struct _Toolview { View parent_class; Toolkitview *kview; GtkWidget *item; /* Menu item we made for this tool */ }; typedef struct _ToolviewClass { ViewClass parent_class; /* My methods. */ } ToolviewClass; GtkType toolview_get_type( void ); View *toolview_new( void ); ================================================ FILE: src/trace.c ================================================ /* trace window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* OR of the flags in all the trace windows. */ TraceFlags trace_flags = (TraceFlags) 0; static LogClass *parent_class = NULL; /* All trace windows. */ static GSList *trace_all = NULL; /* Trace buffer stack. */ static VipsBuf trace_buffer_stack[SPINE_SIZE]; static int trace_buffer_stack_p = 0; /* Number of active trace blocks. */ static int trace_block_count = 0; /* All the trace menus we have. */ typedef struct _TraceTypeMenu { const char *name; TraceFlags flag; } TraceTypeMenu; /* Map action names to trace flags for the radio menu. */ static const TraceTypeMenu trace_types[] = { { "Operator", TRACE_OPERATOR }, { "Builtin", TRACE_BUILTIN }, { "Class", TRACE_CLASS_NEW }, { "VIPS", TRACE_VIPS } }; static TraceFlags trace_get_trace_flag( GtkAction *action ) { const char *name = gtk_action_get_name( action ); int i; for( i = 0; i < IM_NUMBER( trace_types ); i++ ) if( strcmp( name, trace_types[i].name ) == 0 ) return( trace_types[i].flag ); g_assert( FALSE ); /* Keep gcc happy. */ return( FALSE ); } void trace_block( void ) { trace_block_count += 1; } void trace_unblock( void ) { trace_block_count -= 1; g_assert( trace_block_count >= 0 ); } void trace_reset( void ) { int i; for( i = 0; i < trace_buffer_stack_p; i++ ) vips_buf_destroy( &trace_buffer_stack[i] ); trace_buffer_stack_p = 0; } void trace_check( void ) { g_assert( trace_buffer_stack_p == 0 ); } VipsBuf * trace_push( void ) { int i; #ifdef DEBUG printf( "trace_push: %d\n", trace_buffer_stack_p ); #endif if( trace_buffer_stack_p >= SPINE_SIZE ) { error_top( _( "Overflow error." ) ); error_sub( _( "Trace buffer stack overflow." ) ); reduce_throw( reduce_context ); } i = trace_buffer_stack_p++; vips_buf_init_dynamic( &trace_buffer_stack[i], MAX_TRACE ); return( &trace_buffer_stack[i] ); } void trace_pop( void ) { int i; #ifdef DEBUG printf( "trace_pop: %d\n", trace_buffer_stack_p ); #endif g_assert( trace_buffer_stack_p > 0 ); i = --trace_buffer_stack_p; vips_buf_destroy( &trace_buffer_stack[i] ); } VipsBuf * trace_current( void ) { g_assert( trace_buffer_stack_p > 0 ); return( &trace_buffer_stack[trace_buffer_stack_p - 1] ); } int trace_get_mark( void ) { return( trace_buffer_stack_p ); } void trace_pop_to( int n ) { g_assert( n >= 0 && n <= trace_buffer_stack_p ); while( trace_buffer_stack_p > n ) trace_pop(); } static void * trace_global_rethink_sub( Trace *trace ) { trace_flags |= trace->flags; return( NULL ); } /* Rethink the global trace_flags. */ static void trace_global_rethink( void ) { trace_flags = 0; slist_map( trace_all, (SListMapFn) trace_global_rethink_sub, NULL ); } static void trace_destroy( GtkObject *object ) { Trace *trace; g_return_if_fail( object != NULL ); g_return_if_fail( IS_TRACE( object ) ); trace = TRACE( object ); /* My instance destroy stuff. */ trace_all = g_slist_remove( trace_all, trace ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); trace_global_rethink(); } static void trace_view_action_cb( GtkToggleAction *action, Trace *trace ) { TraceFlags flag = trace_get_trace_flag( GTK_ACTION( action ) ); if( gtk_toggle_action_get_active( action ) ) trace->flags |= flag; else trace->flags &= flag ^ ((TraceFlags) -1); trace_global_rethink(); } /* Our actions. */ static GtkActionEntry trace_actions[] = { { "Clear", NULL, N_( "_Clear" ), NULL, N_( "Clear trace window" ), G_CALLBACK( log_clear_action_cb ) } }; static GtkToggleActionEntry trace_toggle_actions[] = { { "Operator", NULL, N_( "_Operators" ), NULL, N_( "trace operators" ), G_CALLBACK( trace_view_action_cb ), FALSE }, { "Builtin", NULL, N_( "_Builtin Functions" ), NULL, N_( "trace calls to built in functions" ), G_CALLBACK( trace_view_action_cb ), FALSE }, { "Class", NULL, N_( "_Class Construction" ), NULL, N_( "trace class constructors" ), G_CALLBACK( trace_view_action_cb ), FALSE }, { "VIPS", NULL, N_( "_VIPS Operations" ), NULL, N_( "trace calls to VIPS" ), G_CALLBACK( trace_view_action_cb ), FALSE } }; static const char *trace_menubar_ui_description = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static void trace_class_init( TraceClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; LogClass *log_class = (LogClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = trace_destroy; log_class->actions = trace_actions; log_class->n_actions = IM_NUMBER( trace_actions ); log_class->toggle_actions = trace_toggle_actions; log_class->n_toggle_actions = IM_NUMBER( trace_toggle_actions ); log_class->action_name = "TraceActions"; log_class->ui_description = trace_menubar_ui_description; log_class->menu_bar_name = "/TraceMenubar"; } static void trace_init( Trace *trace ) { trace->flags = 0; } GtkType trace_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Trace", sizeof( Trace ), sizeof( TraceClass ), (GtkClassInitFunc) trace_class_init, (GtkObjectInitFunc) trace_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_LOG, &info ); } return( type ); } static void trace_link( Trace *trace ) { iwindow_set_title( IWINDOW( trace ), _( "Trace" ) ); gtk_window_set_default_size( GTK_WINDOW( trace ), 640, 480 ); iwindow_set_size_prefs( IWINDOW( trace ), "TRACE_WIDTH", "TRACE_HEIGHT" ); iwindow_build( IWINDOW( trace ) ); trace_all = g_slist_prepend( trace_all, trace ); gtk_widget_show( GTK_WIDGET( trace ) ); } Trace * trace_new( void ) { Trace *trace = gtk_type_new( TYPE_TRACE ); trace_link( trace ); return( trace ); } static void * trace_text_sub( Trace *trace, const char *buf, TraceFlags flags ) { if( !trace_block_count && trace->flags & flags ) log_text( LOG( trace ), buf ); return( NULL ); } void trace_text( TraceFlags flags, const char *fmt, ... ) { va_list ap; char buf[MAX_STRSIZE]; if( !(trace_flags & flags) ) return; va_start( ap, fmt ); (void) im_vsnprintf( buf, MAX_STRSIZE, fmt, ap ); va_end( ap ); slist_map2( trace_all, (SListMap2Fn) trace_text_sub, buf, (void *) flags ); } void trace_pelement( PElement *pe ) { VipsBuf *buf = trace_current(); Heap *heap = reduce_context->heap; graph_pelement( heap, buf, pe, TRACE_FUNCTIONS ); } void trace_node( HeapNode *node ) { Element e; PElement pe; PEPOINTE( &pe, &e ); PEPUTP( &pe, ELEMENT_NODE, node ); trace_pelement( &pe ); } void trace_args( HeapNode **arg, int n ) { VipsBuf *buf = trace_current(); int i; for( i = n - 1; i >= 0; i-- ) { PElement rhs; PEPOINTRIGHT( arg[i], &rhs ); trace_pelement( &rhs ); vips_buf_appends( buf, " " ); } vips_buf_appendf( buf, "->\n" ); } void trace_binop( Compile *compile, PElement *left, BinOp bop, PElement *right ) { VipsBuf *buf = trace_current(); vips_buf_appendf( buf, "\"%s\" ", decode_BinOp( bop ) ); trace_pelement( left ); vips_buf_appends( buf, " " ); trace_pelement( right ); vips_buf_appends( buf, " -> (" ); compile_name( compile, buf ); vips_buf_appends( buf, ")\n" ); } void trace_uop( UnOp uop, PElement *arg ) { VipsBuf *buf = trace_current(); vips_buf_appendf( buf, "\"%s\" ", decode_UnOp( uop ) ); trace_pelement( arg ); vips_buf_appends( buf, " ->\n" ); } void trace_result( TraceFlags flags, PElement *out ) { VipsBuf *buf = trace_current(); vips_buf_appendf( buf, " " ); trace_pelement( out ); vips_buf_appends( buf, "\n" ); trace_text( flags, "%s", vips_buf_all( buf ) ); } ================================================ FILE: src/trace.h ================================================ /* Decls for trace.c ... a trace window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TRACE (trace_get_type()) #define TRACE( obj ) (GTK_CHECK_CAST( (obj), TYPE_TRACE, Trace )) #define TRACE_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_TRACE, TraceClass )) #define IS_TRACE( obj ) (GTK_CHECK_TYPE( (obj), TYPE_TRACE )) #define IS_TRACE_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_TRACE )) /* The various things we can trace. */ typedef enum { TRACE_BUILTIN = 1, /* Calls to built in functions */ TRACE_OPERATOR = 2, /* +, -, etc. */ TRACE_CLASS_NEW = 4, /* Class construction */ TRACE_VIPS = 8 /* VIPS operations */ } TraceFlags; struct _Trace { Log parent_class; TraceFlags flags; }; typedef struct _TraceClass { LogClass parent_class; /* My methods. */ } TraceClass; extern TraceFlags trace_flags; void trace_block( void ); void trace_unblock( void ); void trace_reset( void ); void trace_check( void ); VipsBuf *trace_push( void ); void trace_pop( void ); VipsBuf *trace_current( void ); void trace_pop_to( int n ); int trace_get_mark( void ); GtkType trace_get_type( void ); Trace *trace_new( void ); void trace_text( TraceFlags flags, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void trace_pelement( PElement *pe ); void trace_node( HeapNode *node ); void trace_args( HeapNode **arg, int n ); void trace_binop( Compile *compile, PElement *left, BinOp bop, PElement *right ); void trace_uop( UnOp uop, PElement *arg ); void trace_result( TraceFlags flags, PElement *out ); ================================================ FILE: src/tree.c ================================================ /* Build parse trees. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* Free any stuff attached to a ParseConst. */ void tree_const_destroy( ParseConst *pc ) { if( pc->type == PARSE_CONST_STR ) IM_FREE( pc->val.str ); pc->type = PARSE_CONST_NONE; } void tree_const_copy( ParseConst *from, ParseConst *to ) { *to = *from; if( to->type == PARSE_CONST_STR && to->val.str ) to->val.str = im_strdupn( to->val.str ); } /* Free a parse node. */ void * tree_node_destroy( ParseNode *n ) { switch( n->type ) { case NODE_PATTERN_CLASS: case NODE_TAG: IM_FREE( n->tag ); break; case NODE_CONST: tree_const_destroy( &n->con ); break; case NODE_LISTCONST: case NODE_SUPER: IM_FREEF( g_slist_free, n->elist ); break; case NODE_APPLY: case NODE_CLASS: case NODE_BINOP: case NODE_UOP: case NODE_LEAF: case NODE_GENERATOR: case NODE_NONE: case NODE_COMPOSE: break; default: g_assert( FALSE ); } IM_FREE( n ); return( NULL ); } /* Make an empty parse node. */ static ParseNode * tree_new( Compile *compile ) { ParseNode *no = INEW( NULL, ParseNode ); no->compile = compile; no->type = NODE_NONE; no->biop = BI_NONE; no->uop = UN_NONE; no->arg1 = NULL; no->arg2 = NULL; no->arg3 = NULL; no->leaf = NULL; no->klass = NULL; no->elist = NULL; no->tag = NULL; no->con.type = PARSE_CONST_NONE; no->con.val.str = NULL; compile->treefrag = g_slist_prepend( compile->treefrag, no ); return( no ); } /* Make a binary operator node. */ ParseNode * tree_binop_new( Compile *compile, BinOp op, ParseNode *l, ParseNode *r ) { ParseNode *no = tree_new( compile ); no->type = NODE_BINOP; no->biop = op; no->arg1 = l; no->arg2 = r; return( no ); } /* Make a function compose node. */ ParseNode * tree_compose_new( Compile *compile, ParseNode *f, ParseNode *g ) { ParseNode *no = tree_new( compile ); no->type = NODE_COMPOSE; no->arg1 = f; no->arg2 = g; return( no ); } /* Make a generator node. */ ParseNode * tree_generator_new( Compile *compile, ParseNode *s, ParseNode *n, ParseNode *f ) { ParseNode *no = tree_new( compile ); no->type = NODE_GENERATOR; no->arg1 = s; no->arg2 = n; no->arg3 = f; return( no ); } /* Make an IF node. */ ParseNode * tree_ifelse_new( Compile *compile, ParseNode *c, ParseNode *t, ParseNode *e ) { ParseNode *else_node = tree_lconst_new( compile, e ); ParseNode *then_node = tree_lconst_extend( compile, else_node, t ); ParseNode *if_node = tree_binop_new( compile, BI_IF, c, then_node ); return( if_node ); } /* Make a class node. */ ParseNode * tree_class_new( Compile *compile ) { ParseNode *no = tree_new( compile ); Symbol *this, *super, *name, *cons; g_assert( !compile->is_klass ); g_assert( !compile->this ); g_assert( !compile->super ); no->type = NODE_CLASS; no->klass = compile; /* Make enclosing into a class. */ compile->is_klass = TRUE; /* Add builtin syms. */ this = symbol_new_defining( compile, MEMBER_THIS ); (void) symbol_parameter_builtin_init( this ); compile->this = this; super = symbol_new_defining( compile, MEMBER_SUPER ); (void) symbol_user_init( super ); (void) compile_new_local( super->expr ); symbol_made( super ); compile->super = super; name = symbol_new_defining( compile, MEMBER_NAME ); (void) symbol_parameter_builtin_init( name ); cons = symbol_new_defining( compile, IOBJECT( compile->sym )->name ); (void) symbol_user_init( cons ); (void) compile_new_local( cons->expr ); cons->expr->compile->tree = tree_leafsym_new( compile, compile->sym ); symbol_made( cons ); return( no ); } /* Make a tag node. */ ParseNode * tree_tag_new( Compile *compile, const char *r ) { ParseNode *no = tree_new( compile ); no->type = NODE_TAG; no->tag = im_strdupn( r ); return( no ); } /* Make a unary operator node. */ ParseNode * tree_unop_new( Compile *compile, UnOp op, ParseNode *a ) { ParseNode *no = tree_new( compile ); no->type = NODE_UOP; no->uop = op; no->arg1 = a; return( no ); } ParseNode * tree_leaf_new( Compile *compile, const char *name ) { ParseNode *no = tree_new( compile ); no->type = NODE_LEAF; no->leaf = symbol_new_reference( compile, name ); /* Have we a reference to a ZOMBIE? If yes, we may need to patch this * leaf to point to a new symbol. Add the leaf's pointer to the * refedat list on the ZOMBIE. */ if( no->leaf->type == SYM_ZOMBIE ) (void) symbol_patch_add( (void **) &no->leaf, no->leaf ); return( no ); } /* Make a new leaf node ... except we know the final symbol now. */ ParseNode * tree_leafsym_new( Compile *compile, Symbol *sym ) { ParseNode *no = tree_new( compile ); /* Fill fields. */ no->type = NODE_LEAF; no->leaf = sym; /* Note that this compile refs this sym. */ compile_link_make( compile, sym ); /* Have we a reference to a ZOMBIE? If yes, we may need to patch this * leaf to point to a new symbol. Add the leaf's pointer to the * refedat list on the ZOMBIE. */ if( sym->type == SYM_ZOMBIE ) (void) symbol_patch_add( (void **) &no->leaf, sym ); return( no ); } /* Init a clist. */ ParseNode * tree_lconst_new( Compile *compile, ParseNode *a ) { ParseNode *no = tree_new( compile ); /* Fill fields. */ no->type = NODE_LISTCONST; no->elist = NULL; no->elist = g_slist_prepend( no->elist, a ); return( no ); } /* Extend a clist. */ ParseNode * tree_lconst_extend( Compile *compile, ParseNode *base, ParseNode *new ) { g_assert( base->type == NODE_LISTCONST ); base->elist = g_slist_prepend( base->elist, new ); return( base ); } /* Init a super. */ ParseNode * tree_super_new( Compile *compile ) { ParseNode *no = tree_new( compile ); no->type = NODE_SUPER; return( no ); } /* Extend a super. */ ParseNode * tree_super_extend( Compile *compile, ParseNode *base, ParseNode *new ) { g_assert( base->type == NODE_SUPER ); base->elist = g_slist_append( base->elist, new ); return( base ); } /* Make a new constant node. */ ParseNode * tree_const_new( Compile *compile, ParseConst n ) { ParseNode *no = tree_new( compile ); no->type = NODE_CONST; no->con = n; return( no ); } /* Make a new apply node. */ ParseNode * tree_appl_new( Compile *compile, ParseNode *l, ParseNode *r ) { ParseNode *no = tree_new( compile ); no->type = NODE_APPLY; no->arg1 = l; no->arg2 = r; return( no ); } ParseNode * tree_pattern_class_new( Compile *compile, const char *class_name, ParseNode *l ) { ParseNode *no = tree_new( compile ); no->type = NODE_PATTERN_CLASS; no->arg1 = l; no->tag = im_strdupn( class_name ); return( no ); } ParseNode * tree_map( Compile *compile, tree_map_fn fn, ParseNode *node, void *a, void *b ) { ParseNode *result; GSList *l; g_assert( node ); if( (result = fn( compile, node, a, b )) ) return( result ); switch( node->type ) { case NODE_GENERATOR: if( (result = tree_map( compile, fn, node->arg1, a, b )) ) return( result ); if( node->arg2 && (result = tree_map( compile, fn, node->arg2, a, b )) ) return( result ); if( node->arg3 && (result = tree_map( compile, fn, node->arg3, a, b )) ) return( result ); break; case NODE_APPLY: case NODE_BINOP: case NODE_COMPOSE: if( (result = tree_map( compile, fn, node->arg1, a, b )) || (result = tree_map( compile, fn, node->arg2, a, b )) ) return( result ); break; case NODE_UOP: if( (result = tree_map( compile, fn, node->arg1, a, b )) ) return( result ); break; case NODE_SUPER: case NODE_LISTCONST: for( l = node->elist; l; l = l->next ) { ParseNode *arg = (ParseNode *) l->data; if( (result = tree_map( compile, fn, arg, a, b )) ) return( result ); } break; case NODE_LEAF: case NODE_CLASS: case NODE_TAG: case NODE_CONST: break; case NODE_NONE: default: g_assert( FALSE ); } return( NULL ); } /* Copy a tree to a new context. Make all symbols afresh ... you need to link * after calling this. */ ParseNode * tree_copy( Compile *compile, ParseNode *node ) { ParseNode *copy; GSList *l; g_assert( node ); switch( node->type ) { case NODE_GENERATOR: case NODE_APPLY: case NODE_BINOP: case NODE_COMPOSE: case NODE_UOP: case NODE_TAG: case NODE_CONST: case NODE_PATTERN_CLASS: copy = tree_new( compile ); copy->type = node->type; copy->uop = node->uop; copy->biop = node->biop; if( node->tag ) copy->tag = im_strdupn( node->tag ); tree_const_copy( &node->con, ©->con ); if( node->arg1 ) copy->arg1 = tree_copy( compile, node->arg1 ); if( node->arg2 ) copy->arg2 = tree_copy( compile, node->arg2 ); if( node->arg3 ) copy->arg3 = tree_copy( compile, node->arg3 ); break; case NODE_SUPER: case NODE_LISTCONST: copy = tree_new( compile ); for( l = node->elist; l; l = l->next ) { ParseNode *arg = (ParseNode *) l->data; copy->elist = g_slist_append( copy->elist, tree_copy( compile, arg ) ); } copy->type = node->type; break; case NODE_CLASS: copy = tree_class_new( compile ); break; case NODE_LEAF: copy = tree_leaf_new( compile, IOBJECT( node->leaf )->name ); break; case NODE_NONE: default: copy = NULL; g_assert( FALSE ); } return( copy ); } ================================================ FILE: src/tree.h ================================================ /* Declarations for the tree builder. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* The kinds of nodes we can have in a parse tree. */ typedef enum { NODE_NONE, /* Empty */ NODE_APPLY, /* A function application */ NODE_BINOP, /* A binary operator */ NODE_UOP, /* A unary operator */ NODE_LEAF, /* A leaf .. a symbol of some sort */ NODE_CLASS, /* A class */ NODE_TAG, /* A tag .. rhs of '.' */ NODE_CONST, /* A constant */ NODE_GENERATOR, /* A list generator */ NODE_COMPOSE, /* Function composition */ NODE_SUPER, /* Superclass constructor */ NODE_PATTERN_CLASS, /* A class pattern match */ NODE_LISTCONST /* A list expression "[1, 2, 3]" */ } NodeTypes; /* Binary operators. Order important! Keep changes in step with operator_table[] in action.c */ typedef enum { BI_NONE = 0, /* Nothing */ BI_ADD, /* Addition and subtraction */ BI_SUB, BI_REM, /* Remainder after division */ BI_POW, /* Raise to power */ BI_SELECT, /* Select a channel/subscript */ BI_LSHIFT, /* Shift left */ BI_RSHIFT, /* Shift right */ BI_DIV, /* Divide by a constant */ BI_JOIN, /* Join of two objects */ BI_DOT, /* Projection operator */ BI_COMMA, /* Form complex number */ BI_MUL, /* Mult by a constant */ BI_LAND, /* Logical and */ BI_LOR, /* Logical or */ BI_BAND, /* Bitwise and */ BI_BOR, /* Bitwise or */ BI_EOR, /* Either - or */ BI_EQ, /* Equality */ BI_NOTEQ, BI_PEQ, /* Pointer equality */ BI_PNOTEQ, BI_LESS, /* Relational ops */ BI_LESSEQ, BI_MORE, BI_MOREEQ, BI_IF, /* if-then-else */ BI_CONS /* List cons ... has to be last, see below */ } BinOp; /* Unary operators. Order important! Keep changes in step with operator_table[] in action.c */ typedef enum { UN_NONE = BI_CONS + 1, /* Nothing */ UN_CSCHAR, /* Convert to signed char */ UN_CUCHAR, /* Convert to unsigned char */ UN_CSSHORT, /* Convert to signed short */ UN_CUSHORT, /* Convert to unsigned short */ UN_CSINT, /* Convert to signed int */ UN_CUINT, /* Convert to unsigned int */ UN_CFLOAT, /* Convert to signed float */ UN_CDOUBLE, /* Convert to signed double */ UN_CCOMPLEX, /* Convert to complex */ UN_CDCOMPLEX, /* Convert to double complex */ UN_MINUS, /* Unary minus */ UN_NEG, /* Logical negation, "!" */ UN_COMPLEMENT, /* 1s complement, "~" */ UN_PLUS, /* Unary plus */ UN_LAST /* Sanity check with this */ } UnOp; /* The sorts of constants we can have in expressions. */ typedef enum { PARSE_CONST_NONE, PARSE_CONST_STR, PARSE_CONST_BOOL, PARSE_CONST_NUM, PARSE_CONST_COMPLEX, /* Eg. 12j == (0, 12) */ PARSE_CONST_CHAR, PARSE_CONST_ELIST /* Empty list [] */ } ParseConstTypes; /* Constants in expressions. */ struct _ParseConst { ParseConstTypes type; union { double num; char *str; gboolean bol; int ch; } val; }; /* A parse tree node. */ struct _ParseNode { /* Compiled in here. */ Compile *compile; NodeTypes type; /* Bundle for node types with up to two arguments. */ BinOp biop; UnOp uop; ParseNode *arg1; ParseNode *arg2; /* Just for generators, eg. [a, b .. c] */ ParseNode *arg3; /* A symbol reference. */ Symbol *leaf; /* A class. */ Compile *klass; /* Expression list ... super constructor plus args, or list constant. */ GSList *elist; /* A tag. */ char *tag; /* A constant. */ ParseConst con; }; void tree_const_destroy( ParseConst *pc ); ParseNode *tree_binop_new( Compile *compile, BinOp op, ParseNode *l, ParseNode *r ); ParseNode *tree_generator_new( Compile *compile, ParseNode *s, ParseNode *i, ParseNode *f ); ParseNode *tree_lconst_new( Compile *compile, ParseNode *s ); ParseNode *tree_lconst_extend( Compile *compile, ParseNode *s, ParseNode *n ); ParseNode *tree_super_new( Compile *compile ); ParseNode *tree_super_extend( Compile *compile, ParseNode *base, ParseNode *n ); ParseNode *tree_ifelse_new( Compile *compile, ParseNode *c, ParseNode *t, ParseNode *e ); ParseNode *tree_appl_new( Compile *compile, ParseNode *l, ParseNode *r ); ParseNode *tree_tag_new( Compile *compile, const char *r ); ParseNode *tree_unop_new( Compile *compile, UnOp op, ParseNode *a ); ParseNode *tree_leaf_new( Compile *compile, const char *name ); ParseNode *tree_leafsym_new( Compile *compile, Symbol *sym ); ParseNode *tree_const_new( Compile *compile, ParseConst n ); ParseNode *tree_class_new( Compile *compile ); ParseNode *tree_compose_new( Compile *compile, ParseNode *f, ParseNode *g ); ParseNode *tree_pattern_new( Compile *compile ); ParseNode *tree_pattern_class_new( Compile *compile, const char *class_name, ParseNode *l ); void *tree_node_destroy( ParseNode *n ); typedef ParseNode *(*tree_map_fn)( Compile *, ParseNode *, void *, void * ); ParseNode *tree_map( Compile *compile, tree_map_fn fn, ParseNode *node, void *a, void *b ); /* Copy a tree into a new context. */ ParseNode *tree_copy( Compile *compile, ParseNode *node ); ================================================ FILE: src/tslider.c ================================================ /* a slider with an entry widget */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" /* Our signals. */ enum { CHANGED, ACTIVATE, SLIDER_CHANGED, TEXT_CHANGED, LAST_SIGNAL }; static GtkHBoxClass *parent_class = NULL; static guint tslider_signals[LAST_SIGNAL] = { 0 }; /* Are two doubles more or less equal. We need this when we check the sliders * for update to stop loops. The 0.0001 is a bit of a fudge :-( */ #define DEQ( A, B ) (ABS((A) - (B)) < 0.0001) static void tslider_destroy( GtkObject *object ) { Tslider *tslider; g_return_if_fail( object != NULL ); g_return_if_fail( IS_TSLIDER( object ) ); tslider = TSLIDER( object ); #ifdef DEBUG printf( "tslider_destroy: %p\n", tslider ); #endif /*DEBUG*/ /* My instance destroy stuff. */ if( tslider->adj ) { gtk_signal_disconnect_by_data( GTK_OBJECT( tslider->adj ), (gpointer) tslider ); tslider->adj = NULL; } GTK_OBJECT_CLASS( parent_class )->destroy( object ); } /* Map a value to a slider position. */ static double tslider_value_to_slider( Tslider *tslider, double value ) { /* Map our range to 0-1. */ const double scale = 1.0 / (tslider->to - tslider->from); const double to01 = (value - tslider->from) * scale; /* Pass through user fn. */ const double mapped = tslider->value_to_slider( tslider->from, tslider->to, to01 ); const double nvalue = mapped / scale + tslider->from; #ifdef DEBUG printf( "tslider_value_to_slider: %g, to %g\n", value, nvalue ); #endif /*DEBUG*/ /* Map back to main range. */ return( nvalue ); } /* Map a slider position to a value. */ static double tslider_slider_to_value( Tslider *tslider, double value ) { /* Map our range to 0-1. */ const double scale = 1.0 / (tslider->to - tslider->from); const double to01 = (value - tslider->from) * scale; /* Pass through user fn. */ const double mapped = tslider->slider_to_value( tslider->from, tslider->to, to01 ); const double nvalue = mapped / scale + tslider->from; #ifdef DEBUG printf( "tslider_slider_to_value: %g, to %g\n", value, nvalue ); #endif /*DEBUG*/ /* Map back to main range. */ return( nvalue ); } /* from/to/value have changed ... update the widgets. */ static void tslider_real_changed( Tslider *tslider ) { GtkAdjustment *adj = tslider->adj; GtkWidget *entry = tslider->entry; #ifdef DEBUG printf( "tslider_real_changed: %p, val = %g\n", tslider, tslider->value ); #endif /*DEBUG*/ if( tslider->auto_link ) tslider->svalue = tslider_value_to_slider( tslider, tslider->value ); gtk_signal_handler_block_by_data( GTK_OBJECT( adj ), tslider ); gtk_signal_handler_block_by_data( GTK_OBJECT( entry ), tslider ); /* Some libc's hate out-of-bounds precision, so clip, just in case. */ set_gentry( tslider->entry, "%.*f", IM_CLIP( 0, tslider->digits, 100 ), tslider->value ); gtk_scale_set_digits( GTK_SCALE( tslider->slider ), tslider->digits ); if( !DEQ( tslider->from, tslider->last_from ) || !DEQ( tslider->to, tslider->last_to ) ) { double range = tslider->to - tslider->from; adj->step_increment = range / 100; adj->page_increment = range / 10; adj->page_size = range / 10; adj->lower = tslider->from; adj->upper = tslider->to + adj->page_size; tslider->last_to = tslider->to; tslider->last_from = tslider->from; gtk_adjustment_changed( adj ); } if( !DEQ( tslider->svalue, tslider->last_svalue ) ) { adj->value = tslider->svalue; tslider->last_svalue = tslider->svalue; gtk_adjustment_value_changed( adj ); } gtk_signal_handler_unblock_by_data( GTK_OBJECT( adj ), tslider ); gtk_signal_handler_unblock_by_data( GTK_OBJECT( entry ), tslider ); } static void tslider_class_init( TsliderClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); GtkObjectClass *object_class = (GtkObjectClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = tslider_destroy; class->changed = tslider_real_changed; class->slider_changed = NULL; class->activate = NULL; /* Create signals. */ tslider_signals[CHANGED] = g_signal_new( "changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( TsliderClass, changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); tslider_signals[ACTIVATE] = g_signal_new( "activate", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( TsliderClass, activate ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); tslider_signals[SLIDER_CHANGED] = g_signal_new( "slider_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( TsliderClass, slider_changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); tslider_signals[TEXT_CHANGED] = g_signal_new( "text_changed", G_OBJECT_CLASS_TYPE( gobject_class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( TsliderClass, text_changed ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); /* Init methods. */ } /* From/to/value have changed ... tell everyone. */ void tslider_changed( Tslider *tslider ) { #ifdef DEBUG printf( "tslider_changed\n" ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( tslider ), tslider_signals[CHANGED], 0 ); } /* Activated! */ static void tslider_activate( Tslider *tslider ) { #ifdef DEBUG printf( "tslider_activate\n" ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( tslider ), tslider_signals[ACTIVATE], 0 ); } /* Just the slider changed. */ static void tslider_slider_changed( Tslider *tslider ) { #ifdef DEBUG printf( "tslider_slider_changed\n" ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( tslider ), tslider_signals[SLIDER_CHANGED], 0 ); } /* Text has been touched. */ static void tslider_text_changed( Tslider *tslider ) { #ifdef DEBUG printf( "tslider_text_changed\n" ); #endif /*DEBUG*/ g_signal_emit( G_OBJECT( tslider ), tslider_signals[TEXT_CHANGED], 0 ); } /* Enter in entry widget */ static void tslider_value_activate_cb( GtkWidget *entry, Tslider *tslider ) { double value; if( !get_geditable_double( entry, &value ) ) { iwindow_alert( entry, GTK_MESSAGE_ERROR ); return; } if( tslider->value != value ) { tslider->value = value; if( tslider->auto_link ) tslider_changed( tslider ); else tslider_activate( tslider ); } } /* Drag on slider. */ static void tslider_value_changed_cb( GtkAdjustment *adj, Tslider *tslider ) { #ifdef DEBUG printf( "tslider_value_changed_cb\n" ); #endif /*DEBUG*/ if( tslider->svalue != adj->value ) { tslider->svalue = adj->value; if( tslider->auto_link ) { tslider->value = tslider_slider_to_value( tslider, adj->value ); tslider_changed( tslider ); } else tslider_slider_changed( tslider ); } } /* Text has changed (and may need to be scanned later). */ static void tslider_text_changed_cb( GtkWidget *widget, Tslider *tslider ) { #ifdef DEBUG printf( "tslider_text_changed_cb\n" ); #endif /*DEBUG*/ tslider_text_changed( tslider ); } /* Default identity conversion. */ static double tslider_conversion_id( double from, double to, double value ) { return( value ); } static gboolean tslider_scroll_cb( GtkWidget *wid, GdkEvent *event, Tslider *tslider ) { gboolean handled; handled = FALSE; /* Stop any other scroll handlers running. We don't want the scroll * wheel to change widgets while we're moving. */ if( tslider->ignore_scroll ) handled = TRUE; return( handled ); } static void tslider_init( Tslider *tslider ) { #ifdef DEBUG printf( "tslider_init: %p\n", tslider ); #endif /*DEBUG*/ /* Any old start values ... overridden later. */ tslider->from = -1; tslider->to = -1; tslider->value = -1; tslider->svalue = -1; tslider->digits = -1; tslider->last_to = -1; tslider->last_from = -1; tslider->last_svalue = -1; tslider->ignore_scroll = TRUE; gtk_box_set_spacing( GTK_BOX( tslider ), 2 ); tslider->entry = build_entry( 5 ); gtk_entry_set_max_length( GTK_ENTRY( tslider->entry ), 10 ); set_tooltip( tslider->entry, _( "Slider value ... edit!" ) ); gtk_box_pack_start( GTK_BOX( tslider ), tslider->entry, FALSE, FALSE, 0 ); gtk_signal_connect( GTK_OBJECT( tslider->entry ), "activate", GTK_SIGNAL_FUNC( tslider_value_activate_cb ), tslider ); gtk_signal_connect( GTK_OBJECT( tslider->entry ), "changed", GTK_SIGNAL_FUNC( tslider_text_changed_cb ), tslider ); gtk_widget_show( tslider->entry ); tslider->slider = gtk_hscale_new( NULL ); tslider->adj = gtk_range_get_adjustment( GTK_RANGE( tslider->slider ) ); gtk_range_set_update_policy( GTK_RANGE( tslider->slider ), GTK_UPDATE_CONTINUOUS ); #ifdef DEBUG gtk_range_set_update_policy( GTK_RANGE( tslider->slider ), GTK_UPDATE_DISCONTINUOUS ); #endif /*DEBUG*/ gtk_scale_set_draw_value( GTK_SCALE( tslider->slider ), FALSE ); gtk_widget_set_size_request( GTK_WIDGET( tslider->slider ), 100, -1 ); gtk_box_pack_start( GTK_BOX( tslider ), tslider->slider, TRUE, TRUE, 0 ); set_tooltip( tslider->slider, _( "Left-drag to set number" ) ); gtk_signal_connect( GTK_OBJECT( tslider->adj ), "value_changed", GTK_SIGNAL_FUNC( tslider_value_changed_cb ), tslider ); g_signal_connect( tslider->slider, "scroll-event", G_CALLBACK( tslider_scroll_cb ), tslider ); gtk_widget_show( tslider->slider ); tslider->auto_link = TRUE; tslider->slider_to_value = tslider_conversion_id; tslider->value_to_slider = tslider_conversion_id; } GtkType tslider_get_type( void ) { static GtkType tslider_type = 0; if( !tslider_type ) { static const GtkTypeInfo sinfo = { "Tslider", sizeof( Tslider ), sizeof( TsliderClass ), (GtkClassInitFunc) tslider_class_init, (GtkObjectInitFunc) tslider_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; tslider_type = gtk_type_unique( GTK_TYPE_HBOX, &sinfo ); } return( tslider_type ); } Tslider * tslider_new() { Tslider *tslider = gtk_type_new( TYPE_TSLIDER ); return( tslider ); } void tslider_set_conversions( Tslider *tslider, tslider_fn value_to_slider, tslider_fn slider_to_value ) { tslider->value_to_slider = value_to_slider; tslider->slider_to_value = slider_to_value; tslider->auto_link = value_to_slider && slider_to_value; } double tslider_log_value_to_slider( double from, double to, double value ) { /* What does 1.0 map to on our [0,1] scale? */ const double mapped1 = (1.0 - from) / (to - from); /* We want an exponent which maps the mid point on the slider to 1. */ const double a = log( mapped1 ) / log( 0.5 ); const double nvalue = pow( value, 1.0 / a ); return( nvalue ); } double tslider_log_slider_to_value( double from, double to, double value ) { /* What does 1.0 map to on our [0,1] scale? */ const double mapped1 = (1.0 - from) / (to - from); /* We want an exponent which maps the mid point on the slider to 1. */ const double a = log( mapped1 ) / log( 0.5 ); const double nvalue = pow( value, a ); return( nvalue ); } void tslider_set_ignore_scroll( Tslider *tslider, gboolean ignore_scroll ) { tslider->ignore_scroll = ignore_scroll; } ================================================ FILE: src/tslider.h ================================================ /* A slider with an entry widget. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_TSLIDER (tslider_get_type()) #define TSLIDER( obj ) (GTK_CHECK_CAST( (obj), TYPE_TSLIDER, Tslider )) #define TSLIDER_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_TSLIDER, TsliderClass )) #define IS_TSLIDER( obj ) (GTK_CHECK_TYPE( (obj), TYPE_TSLIDER )) #define IS_TSLIDER_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_TSLIDER )) typedef double (*tslider_fn)( double from, double to, double value ); typedef struct _Tslider { GtkHBox parent_class; /* Our state. */ double from; double to; double value; /* Real value, as displayed in text */ double svalue; /* Slider value ... secret linear scale */ int digits; /* How many sf to display */ /* Keep last from/to/value settings here. Can't * use from/to since double and float don't compare reliably. */ double last_from, last_to, last_svalue; GtkWidget *entry; GtkWidget *slider; GtkAdjustment *adj; /* Optional functions ... how to make a value from a slider * position, how to make a slider position from a value. * If these are defined, text and slider are linked for you. */ gboolean auto_link; tslider_fn value_to_slider; tslider_fn slider_to_value; /* Ignore scroll events. In workspaces, we want the scroll-wheel to * just scroll the workspace and not adjust sliders. */ gboolean ignore_scroll; } Tslider; typedef struct _TsliderClass { GtkHBoxClass parent_class; void (*changed)( Tslider * ); /* from/to/value change */ void (*activate)( Tslider * ); /* enter in text */ void (*slider_changed)( Tslider * ); /* slider drag */ void (*text_changed)( Tslider * ); /* text has been touched */ } TsliderClass; void tslider_changed( Tslider * ); GtkType tslider_get_type( void ); Tslider *tslider_new( void ); void tslider_set_conversions( Tslider *tslider, tslider_fn value_to_slider, tslider_fn slider_to_value ); double tslider_log_value_to_slider( double from, double to, double value ); double tslider_log_slider_to_value( double from, double to, double value ); void tslider_set_ignore_scroll( Tslider *tslider, gboolean ignore_scroll ); ================================================ FILE: src/util.c ================================================ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Handy for tracing errors. #define DEBUG_ERROR */ /* Prettyprint xml save files. FIXME ... slight mem leak, and save files are much larger ... but leave it on for convenience */ #define DEBUG_SAVEFILE #include "ip.h" /* Track errors messages in this thing. We keep two messages: a principal * error, and extra informative text. For example "Unable to load file.", * "Read failed for file blah, permission denied.". */ static char error_top_text[MAX_STRSIZE]; static char error_sub_text[MAX_STRSIZE]; VipsBuf error_top_buf = VIPS_BUF_STATIC( error_top_text ); VipsBuf error_sub_buf = VIPS_BUF_STATIC( error_sub_text ); /* Useful: Error message and quit. Emergencies only ... we don't tidy up * properly. */ void error( const char *fmt, ... ) { va_list args; fprintf( stderr, IP_NAME ": " ); va_start( args, fmt ); (void) vfprintf( stderr, fmt, args ); va_end( args ); fprintf( stderr, "\n" ); #ifdef DEBUG /* Make a coredump. */ abort(); #endif /*DEBUG*/ exit( 1 ); } /* Set this to block error messages. Useful if we've found an error, we want * to clean up, but we don't want any of the clean-up code to touch the error * buffer. */ static int error_level = 0; void error_block( void ) { error_level++; } void error_unblock( void ) { g_assert( error_level ); error_level--; } /* Set an error buffer. */ static void error_set( VipsBuf *buf, const char *fmt, va_list ap ) { if( !error_level ) { char txt[MAX_STRSIZE]; VipsBuf tmp = VIPS_BUF_STATIC( txt ); /* The string we write may contain itself ... write to an * intermediate, then copy to main. */ vips_buf_vappendf( &tmp, fmt, ap ); vips_buf_rewind( buf ); (void) vips_buf_appends( buf, vips_buf_all( &tmp ) ); #ifdef DEBUG_ERROR printf( "error: %p %s\n", buf, vips_buf_all( buf ) ); #endif /*DEBUG_ERROR*/ } } void error_clear_nip( void ) { if( !error_level ) { vips_buf_rewind( &error_top_buf ); vips_buf_rewind( &error_sub_buf ); #ifdef DEBUG_ERROR printf( "error_clear_nip\n" ); #endif /*DEBUG_ERROR*/ } } void error_clear( void ) { if( !error_level ) { error_clear_nip(); im_error_clear(); } } void error_top( const char *fmt, ... ) { va_list ap; va_start( ap, fmt ); error_set( &error_top_buf, fmt, ap ); va_end( ap ); /* We could use error_clear_nip() before calling error_set(), but that * fails if the arg to us uses the contents of either error buffer. */ if( !error_level ) vips_buf_rewind( &error_sub_buf ); } void error_sub( const char *fmt, ... ) { va_list ap; va_start( ap, fmt ); error_set( &error_sub_buf, fmt, ap ); va_end( ap ); } /* Append any VIPS errors to sub buffer. */ void error_vips( void ) { if( !error_level && strlen( im_error_buffer() ) > 0 ) { if( !vips_buf_is_empty( &error_sub_buf ) ) (void) vips_buf_appendf( &error_sub_buf, "\n" ); (void) vips_buf_appendf( &error_sub_buf, "%s", im_error_buffer() ); im_error_clear(); } } void error_vips_all( void ) { error_top( _( "VIPS library error." ) ); error_vips(); } const char *error_get_top( void ) { return( vips_buf_all( &error_top_buf ) ); } const char *error_get_sub( void ) { return( vips_buf_all( &error_sub_buf ) ); } /* Set an xml property printf() style. */ gboolean set_prop( xmlNode *xnode, 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( xnode, (xmlChar *) name, (xmlChar *) value ) ) { error_top( _( "Unable to set XML property." ) ); error_sub( _( "Unable to set property \"%s\" " "to value \"%s\"." ), name, value ); return( FALSE ); } return( TRUE ); } /* Set an xml property from an optionally NULL string. */ gboolean set_sprop( xmlNode *xnode, const char *name, const char *value ) { if( value && !set_prop( xnode, name, "%s", value ) ) return( FALSE ); return( TRUE ); } /* Set an xml property from an optionally NULL string. */ gboolean set_iprop( xmlNode *xnode, const char *name, int value ) { return( set_prop( xnode, name, "%d", value ) ); } /* Save a list of strings. For name=="fred" and n strings in list, save as * "fredn" == n, "fred0" == list[0], etc. */ gboolean set_slprop( xmlNode *xnode, const char *name, GSList *labels ) { if( labels ) { char buf[256]; int i; (void) im_snprintf( buf, 256, "%sn", name ); if( !set_prop( xnode, buf, "%d", g_slist_length( labels ) ) ) return( FALSE ); for( i = 0; labels; i++, labels = labels->next ) { const char *label = (const char *) labels->data; (void) im_snprintf( buf, 256, "%s%d", name, i ); if( !set_sprop( xnode, buf, label ) ) return( FALSE ); } } return( TRUE ); } /* Set a double ... use non-localisable conversion, rather than %g. */ gboolean set_dprop( xmlNode *xnode, const char *name, double value ) { char buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr( buf, sizeof( buf ), value ); return( set_sprop( xnode, name, buf ) ); } /* Save an array of double. For name=="fred" and n doubles in array, save as * "fredn" == n, "fred0" == array[0], etc. */ gboolean set_dlprop( xmlNode *xnode, const char *name, double *values, int n ) { char buf[256]; int i; (void) im_snprintf( buf, 256, "%sn", name ); if( !set_prop( xnode, buf, "%d", n ) ) return( FALSE ); for( i = 0; i < n; i++ ) { (void) im_snprintf( buf, 256, "%s%d", name, i ); if( !set_dprop( xnode, buf, values[i] ) ) return( FALSE ); } return( TRUE ); } gboolean get_sprop( xmlNode *xnode, const char *name, char *buf, int sz ) { char *value = (char *) xmlGetProp( xnode, (xmlChar *) name ); if( !value ) return( FALSE ); im_strncpy( buf, value, sz ); IM_FREEF( xmlFree, value ); return( TRUE ); } gboolean get_spropb( xmlNode *xnode, const char *name, VipsBuf *buf ) { char *value = (char *) xmlGetProp( xnode, (xmlChar *) name ); if( !value ) return( FALSE ); vips_buf_appends( buf, value ); IM_FREEF( xmlFree, value ); return( TRUE ); } gboolean get_iprop( xmlNode *xnode, const char *name, int *out ) { char buf[256]; if( !get_sprop( xnode, name, buf, 256 ) ) return( FALSE ); *out = atoi( buf ); return( TRUE ); } gboolean get_dprop( xmlNode *xnode, const char *name, double *out ) { char buf[256]; if( !get_sprop( xnode, name, buf, 256 ) ) return( FALSE ); *out = g_ascii_strtod( buf, NULL ); return( TRUE ); } gboolean get_bprop( xmlNode *xnode, const char *name, gboolean *out ) { char buf[256]; if( !get_sprop( xnode, name, buf, 256 ) ) return( FALSE ); *out = strcasecmp( buf, "true" ) == 0; return( TRUE ); } /* Load a list of strings. For name=="fred", look for "fredn" == number of * strings to load, "fred0" == list[0], etc. */ gboolean get_slprop( xmlNode *xnode, const char *name, GSList **out ) { char buf[256]; int n, i; (void) im_snprintf( buf, 256, "%sn", name ); if( !get_iprop( xnode, buf, &n ) ) return( FALSE ); *out = NULL; for( i = n - 1; i >= 0; i-- ) { (void) im_snprintf( buf, 256, "%s%d", name, i ); if( !get_sprop( xnode, buf, buf, 256 ) ) return( FALSE ); *out = g_slist_prepend( *out, g_strdup( buf ) ); } return( TRUE ); } /* Load an array of double. For name=="fred", look for "fredn" == length of * array, "fred0" == array[0], etc. */ gboolean get_dlprop( xmlNode *xnode, const char *name, double **out ) { char buf[256]; int n, i; (void) im_snprintf( buf, 256, "%sn", name ); if( !get_iprop( xnode, buf, &n ) ) return( FALSE ); if( !(*out = IARRAY( NULL, n, double )) ) return( FALSE ); for( i = 0; i < n; i++ ) { (void) im_snprintf( buf, 256, "%s%d", name, i ); if( !get_dprop( xnode, buf, *out + i ) ) return( FALSE ); } return( TRUE ); } /* Find the first child node with a name. */ 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 ); } static int rect_n_rects = 0; /* Allocate and free rects. */ Rect * rect_dup( Rect *init ) { Rect *new_rect; if( !(new_rect = INEW( NULL, Rect )) ) return( NULL ); *new_rect = *init; rect_n_rects += 1; return( new_rect ); } void * rect_free( Rect *rect ) { im_free( rect ); rect_n_rects -= 1; return( NULL ); } /* Test two lists for eqality. */ gboolean 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. */ void * slist_map( GSList *list, SListMapFn fn, gpointer a ) { GSList *copy; GSList *i; void *result; copy = g_slist_copy( list ); result = NULL; for( i = copy; i && !(result = fn( i->data, a )); i = i->next ) ; g_slist_free( copy ); return( result ); } void * slist_map2( GSList *list, SListMap2Fn fn, gpointer a, gpointer 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 * slist_map3( GSList *list, SListMap3Fn fn, gpointer a, gpointer b, gpointer c ) { GSList *copy; GSList *i; void *result; copy = g_slist_copy( list ); result = NULL; for( i = copy; i && !(result = fn( i->data, a, b, c )); i = i->next ) ; g_slist_free( copy ); return( result ); } void * slist_map4( GSList *list, SListMap4Fn fn, gpointer a, gpointer b, gpointer c, gpointer 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 ); } void * slist_map5( GSList *list, SListMap5Fn fn, gpointer a, gpointer b, gpointer c, gpointer d, gpointer e ) { 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, e )); i = i->next ) ; g_slist_free( copy ); return( result ); } /* Map backwards. */ void * slist_map_rev( GSList *list, SListMapFn fn, gpointer a ) { 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 )); i = i->next ) ; g_slist_free( copy ); return( result ); } void * slist_map2_rev( GSList *list, SListMap2Fn fn, gpointer a, gpointer 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 * slist_map3_rev( GSList *list, SListMap3Fn fn, void *a, void *b, void *c ) { 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, c )); i = i->next ) ; g_slist_free( copy ); return( result ); } void * map_equal( void *a, void *b ) { if( a == b ) return( a ); return( NULL ); } void * slist_fold( GSList *list, void *start, SListFoldFn fn, void *a ) { void *c; GSList *ths, *next; for( c = start, ths = list; ths; ths = next ) { next = ths->next; if( !(c = fn( ths->data, c, a )) ) return( NULL ); } return( c ); } void * slist_fold2( GSList *list, void *start, SListFold2Fn fn, void *a, void *b ) { void *c; GSList *ths, *next; for( c = start, ths = list; ths; ths = next ) { next = ths->next; if( !(c = fn( ths->data, c, a, b )) ) return( NULL ); } return( c ); } static void slist_free_all_cb( gpointer thing, gpointer dummy ) { g_free( thing ); } /* Free a g_slist of things which need g_free()ing. */ void slist_free_all( GSList *list ) { g_slist_foreach( list, slist_free_all_cb, NULL ); g_slist_free( list ); } /* Remove all occurences of an item from a list. */ GSList * slist_remove_all( GSList *list, gpointer data ) { GSList *tmp; GSList *prev; prev = NULL; tmp = list; while( tmp ) { if( tmp->data == data ) { 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 ); } Queue * queue_new( void ) { Queue *q = INEW( NULL, Queue ); q->list = NULL; q->tail = NULL; q->length = 0; return( q ); } void * queue_head( Queue *q ) { void *data; g_assert( q ); if( !q->list ) return( NULL ); data = q->list->data; q->list = g_slist_remove( q->list, data ); if( !q->list ) q->tail = NULL; q->length -= 1; return( data ); } void queue_add( Queue *q, void *data ) { if( !q->tail ) { g_assert( !q->list ); q->list = q->tail = g_slist_append( q->list, data ); } else { g_assert( q->list ); q->tail = g_slist_append( q->tail, data ); q->tail = q->tail->next; } q->length += 1; } /* Not very fast! But used infrequently. TRUE if the data was in the queue and * has now been removed. */ gboolean queue_remove( Queue *q, void *data ) { GSList *ele; if( !(ele = g_slist_find( q->list, data )) ) return( FALSE ); q->list = g_slist_remove( q->list, data ); if( ele == q->tail ) q->tail = g_slist_last( q->list ); q->length -= 1; return( TRUE ); } int queue_length( Queue *q ) { return( q->length ); } /* Make an info string about an image. */ void vips_buf_appendi( VipsBuf *buf, IMAGE *im ) { if( !im ) { vips_buf_appends( buf, _( "(no image)" ) ); return; } /* Coded? Special warning. */ if( im->Coding != IM_CODING_NONE ) vips_buf_appendf( buf, "%s, ", NN( im_Coding2char( im->Coding ) ) ); /* Format string expands to (eg.) * "2000x3000 128-bit complex, 3 bands, Lab" */ vips_buf_appendf( buf, ngettext( "%dx%d %s, %d band, %s", "%dx%d %s, %d bands, %s", im->Bands ), im->Xsize, im->Ysize, decode_bandfmt( im->BandFmt ), im->Bands, decode_type( im->Type ) ); } /* Append a string, escaping C stuff. Escape double quotes if quote is set. */ gboolean vips_buf_appendsc( VipsBuf *buf, gboolean quote, const char *str ) { char buffer[FILENAME_MAX]; char buffer2[FILENAME_MAX]; /* /2 to leave a bit of space. */ im_strncpy( buffer, str, FILENAME_MAX / 2 ); /* FIXME ... possible buffer overflow :-( */ my_strecpy( buffer2, buffer, quote ); return( vips_buf_appends( buf, buffer2 ) ); } /* Test for string a ends string b. */ gboolean is_postfix( const char *a, const char *b ) { int n = strlen( a ); int m = strlen( b ); if( m < n ) return( FALSE ); return( !strcmp( a, b + m - n ) ); } /* Test for string a ends string b, case independent. */ gboolean is_casepostfix( const char *a, const char *b ) { int n = strlen( a ); int m = strlen( b ); if( m < n ) return( FALSE ); return( !strcasecmp( a, b + m - n ) ); } /* Test for string a starts string b. */ gboolean is_prefix( 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 ); } /* Test for string a starts string b ... case insensitive. */ gboolean is_caseprefix( 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( toupper( a[i] ) != toupper( b[i] ) ) return( FALSE ); return( TRUE ); } /* Like strstr(), but case-insensitive. */ char * my_strcasestr( const char *haystack, const char *needle ) { int i; int hlen = strlen( haystack ); int nlen = strlen( needle ); for( i = 0; i <= hlen - nlen; i++ ) if( is_caseprefix( needle, haystack + i ) ) return( (char *) (haystack + i) ); return( NULL ); } /* Copy a string, interpreting (a few) C-language escape codes. FIXME ... yuk! dangerous and not that useful, needs a proper function to do this */ char * my_strccpy( char *output, const char *input ) { const char *p; char *q; for( p = input, q = output; *p; p++, q++ ) if( p[0] == '\\' ) { switch( p[1] ) { case 'n': q[0] = '\n'; break; case 't': q[0] = '\t'; break; case 'r': q[0] = '\r'; break; case '"': q[0] = '\"'; break; case '\'': q[0] = '\''; break; case '\\': q[0] = '\\'; break; default: /* Leave uninterpreted. */ q[0] = p[0]; q[1] = p[1]; q++; break; } p++; } else q[0] = p[0]; q[0] = '\0'; return( output ); } /* Copy a string, expanding escape characters into C-language escape codes. * Escape double quotes if quote is set. */ char * my_strecpy( char *output, const char *input, gboolean quote ) { const char *p; char *q; for( p = input, q = output; *p; p++, q++ ) switch( p[0] ) { case '\n': q[0] = '\\'; q[1] = 'n'; q++; break; case '\t': q[0] = '\\'; q[1] = 't'; q++; break; case '\r': q[0] = '\\'; q[1] = 'r'; q++; break; case '\"': if( quote ) { q[0] = '\\'; q[1] = '\"'; q++; } else q[0] = p[0]; break; case '\'': q[0] = '\\'; q[1] = '\''; q++; break; case '\\': q[0] = '\\'; q[1] = '\\'; q++; break; default: q[0] = p[0]; break; } q[0] = '\0'; return( output ); } /* Is a character in a string? */ static int instr( char c, const char *spn ) { const char *p; for( p = spn; *p; p++ ) if( *p == c ) return( 1 ); return( 0 ); } /* Doh ... not everyone has strrspn(), define one. Return a pointer to the * start of the trailing segment of p which contains only chars in spn. */ const char * my_strrspn( const char *p, const char *spn ) { const char *p1; for( p1 = p + strlen( p ) - 1; p1 >= p && instr( *p1, spn ); p1-- ) ; p1++; return( p1 ); } /* Find a pointer to the start of the trailing segment of p which contains * only chars not in spn. */ const char * my_strrcspn( const char *p, const char *spn ) { const char *p1; for( p1 = p + strlen( p ) - 1; p1 >= p && !instr( *p1, spn ); p1-- ) ; p1++; return( p1 ); } /* Find the rightmost occurence of string a in string b. */ const char * findrightmost( const char *a, const char *b ) { int la = strlen( a ); int lb = strlen( b ); int i; if( lb < la ) return( NULL ); for( i = lb - la; i > 0; i-- ) if( strncmp( a, &b[i], la ) == 0 ) return( &b[i] ); return( NULL ); } /* Useful transformation: strip off a set of suffixes (eg. ".v", ".icon", * ".hr"), add a single new suffix (eg. ".hr.v"). */ void change_suffix( const char *name, char *out, const char *new, const char **olds, int nolds ) { char *p; int i; /* Copy start string. */ strcpy( out, name ); /* Drop all matching suffixes. */ while( (p = strrchr( out, '.' )) ) { /* Found suffix - test against list of alternatives. Ignore * case. */ for( i = 0; i < nolds; i++ ) if( strcasecmp( p, olds[i] ) == 0 ) { *p = '\0'; break; } /* Found match? If not, break from loop. */ if( *p ) break; } /* Add new suffix. */ strcat( out, new ); } /* Drop leading and trim trailing non-alphanum characters. NULL if nothing * left. The result can be a variable name. */ char * trim_nonalpha( char *text ) { char *p, *q; /* Skip any initial non-alpha characters. */ for( q = text; *q && !isalpha( (int)(*q) ); q++ ) ; /* Find next non-alphanumeric character. */ for( p = q; *p && isalnum( (int)(*p) ); p++ ) ; *p = '\0'; if( strlen( q ) == 0 ) return( NULL ); else return( q ); } /* Drop leading and trim trailing whitespace characters. NULL if nothing * left. */ char * trim_white( char *text ) { char *p, *q; /* Skip any initial whitespace characters. */ for( q = text; *q && isspace( (int)(*q) ); q++ ) ; /* Find rightmost non-space char. */ for( p = q + strlen( q ) - 1; p > q && isspace( (int)(*p) ); p-- ) ; p[1] = '\0'; if( strlen( q ) == 0 ) return( NULL ); else return( q ); } /* Get a pointer to a band element in a region. */ void * get_element( REGION *ireg, int x, int y, int b ) { IMAGE *im = ireg->im; /* Return a pointer to this on error. */ static PEL empty[50] = { 0 }; PEL *data; int es = IM_IMAGE_SIZEOF_ELEMENT( im ); Rect iarea; /* Make sure we can read from this descriptor. */ if( im_pincheck( im ) ) /* Help! */ return( empty ); /* Ask for the area we need. */ iarea.left = x; iarea.top = y; iarea.width = 1; iarea.height = 1; if( im_prepare( ireg, &iarea ) ) return( empty ); /* Find a pointer to the start of the data. */ data = (PEL *) IM_REGION_ADDR( ireg, x, y ) + b * es; return( (void *) data ); } /* Decode band formats in a friendly way. */ static const char *bandfmt_names[] = { N_( "8-bit unsigned integer" ), /* IM_BANDFMT_UCHAR */ N_( "8-bit signed integer" ), /* IM_BANDFMT_CHAR */ N_( "16-bit unsigned integer" ),/* IM_BANDFMT_USHORT */ N_( "16-bit signed integer" ), /* IM_BANDFMT_SHORT */ N_( "32-bit unsigned integer" ),/* IM_BANDFMT_UINT */ N_( "32-bit signed integer" ), /* IM_BANDFMT_INT */ N_( "32-bit float" ), /* IM_BANDFMT_FLOAT */ N_( "64-bit complex" ), /* IM_BANDFMT_COMPLEX */ N_( "64-bit float" ), /* IM_BANDFMT_DOUBLE */ N_( "128-bit complex" ) /* IM_BANDFMT_DPCOMPLEX */ }; static const int nbandfmt_names = IM_NUMBER( bandfmt_names ); const char * decode_bandfmt( int f ) { if( f > nbandfmt_names - 1 || f < 0 ) return( _( "" ) ); else return( _( bandfmt_names[f] ) ); } /* Decode type names in a way consistent with the menus. */ static const char *type_names[] = { "multiband", /* IM_TYPE_MULTIBAND 0 */ "mono", /* IM_TYPE_B_W 1 */ "luminance", /* LUMINACE 2 */ "xray", /* XRAY 3 */ "infrared", /* IR 4 */ "Yuv", /* YUV 5 */ "red_only", /* RED_ONLY 6 */ "green_only", /* GREEN_ONLY 7 */ "blue_only", /* BLUE_ONLY 8 */ "power_spectrum", /* POWER_SPECTRUM 9 */ "histogram", /* IM_TYPE_HISTOGRAM 10 */ "lookup_table", /* LUT 11 */ "XYZ", /* IM_TYPE_XYZ 12 */ "Lab", /* IM_TYPE_LAB 13 */ "CMC", /* CMC 14 */ "CMYK", /* IM_TYPE_CMYK 15 */ "LabQ", /* IM_TYPE_LABQ 16 */ "RGB", /* IM_TYPE_RGB 17 */ "UCS", /* IM_TYPE_UCS 18 */ "LCh", /* IM_TYPE_LCH 19 */ "", /* ?? 20 */ "LabS", /* IM_TYPE_LABS 21 */ "sRGB", /* IM_TYPE_sRGB 22 */ "Yxy", /* IM_TYPE_YXY 23 */ "Fourier", /* IM_TYPE_FOURIER 24 */ "RGB16", /* IM_TYPE_RGB16 25 */ "GREY16", /* IM_TYPE_GREY16 26 */ "Array", /* VIPS_INTERPRETATION_ARRAY = 27 */ "scRGB" /* VIPS_INTERPRETATION_scRGB = 28 */ }; static const int ntype_names = IM_NUMBER( type_names ); const char * decode_type( int t ) { if( t > ntype_names - 1 || t < 0 ) return( _( "" ) ); else return( type_names[t] ); } /* Make an info string about a file. */ void get_image_info( VipsBuf *buf, const char *name ) { char name2[FILENAME_MAX]; struct stat st; expand_variables( name, name2 ); vips_buf_appendf( buf, "%s, ", im_skip_dir( name ) ); /* Read size and file/dir. */ if( stat( name2, &st ) == -1 ) { vips_buf_appendf( buf, "%s", g_strerror( errno ) ); return; } if( S_ISDIR( st.st_mode ) ) vips_buf_appends( buf, _( "directory" ) ); else if( S_ISREG( st.st_mode ) ) { IMAGE *im; /* Spot workspace files from the filename. These are XML files * and if imagemagick sees them it'll try to load them as SVG * or somethiing awful like that. */ if( is_file_type( &filesel_wfile_type, name2 ) ) { vips_buf_appends( buf, _( "workspace" ) ); } else if( (im = im_open( name2, "r" )) ) { vips_buf_appendi( buf, im ); im_close( im ); } else /* No idea wtf this is, just put the size in. */ vips_buf_append_size( buf, st.st_size ); } } /* A char that can be part of an environment variable name? A-Za-z0-9_ */ static gboolean isvariable( int ch ) { return( isalnum( ch ) || ch == '_' ); } /* Expand environment variables from in to out. Return true if we performed an * expansion, false for no variables there. */ static gboolean expand_once( char *in, char *out ) { char *p, *q; gboolean have_substituted = FALSE; /* Scan and copy. */ for( p = in, q = out; (*q = *p) && (q - out) < FILENAME_MAX; p++, q++ ) /* Did we just copy a '$'? */ if( *p == '$' ) { char vname[FILENAME_MAX]; char *r; const char *subst; const char *s; /* Extract the variable name. */ p++; for( r = vname; isvariable( (int)(*r = *p) ) && (r - vname) < FILENAME_MAX; p++, r++ ) ; *r = '\0'; p--; /* Look up variable. */ subst = g_getenv( vname ); /* Copy variable contents. */ if( subst ) { for( s = subst; (*q = *s) && (q - out) < FILENAME_MAX; s++, q++ ) ; } q--; /* Remember we have performed a substitution. */ have_substituted = TRUE; } /* Or a '~' at the start of the string? */ else if( *p == '~' && p == in ) { const char *subst = g_getenv( "HOME" ); const char *r; /* Copy variable contents. */ if( subst ) { for( r = subst; (*q = *r) && (q - out) < FILENAME_MAX; r++, q++ ) ; } q--; /* Remember we have performed a substitution. */ have_substituted = TRUE; } return( have_substituted ); } /* Expand all variables! Don't touch in, assume out[] is at least * FILENAME_MAX bytes. in and out must not point to the same place! */ void expand_variables( const char *in, char *out ) { char buf[FILENAME_MAX]; char *p1 = (char *) in; /* Discard const, but safe */ char *p2 = out; g_assert( in != out ); /* Expand any environment variables in component. */ while( expand_once( p1, p2 ) ) /* We have expanded --- swap the buffers and try * again. */ if( p2 == out ) { p1 = out; p2 = buf; } else { p1 = buf; p2 = out; } } static void swap_chars( char *buf, char from, char to ) { int i; for( i = 0; buf[i]; i++ ) if( buf[i] == from ) buf[i] = to; } /* If we use '/' seps, swap all '\' for '/' ... likewise vice versa. Only in * the filename part, though. We don't want to junk '\,', for example. */ void nativeize_path( char *buf ) { char filename[FILENAME_MAX]; char mode[FILENAME_MAX]; im_filename_split( buf, filename, mode ); if( G_DIR_SEPARATOR == '/' ) swap_chars( filename, '\\', '/' ); else swap_chars( filename, '/', '\\' ); if( strcmp( mode, "" ) != 0 ) im_snprintf( buf, FILENAME_MAX, "%s:%s", filename, mode ); else im_snprintf( buf, FILENAME_MAX, "%s", filename ); } /* Change all occurences of "from" into "to". This will loop if "to" contains * "from", beware. */ static void swap_string( char *buffer, const char *from, const char *to ) { char *p; while( (p = strstr( buffer, from )) ) { int off = p - buffer; char buf2[FILENAME_MAX]; im_strncpy( buf2, buffer, FILENAME_MAX ); buf2[off] = '\0'; im_snprintf( buffer, FILENAME_MAX, "%s%s%s", buf2, to, buf2 + off + strlen( from ) ); } } /* Remove "." and ".." from an absolute path (if we can). */ void canonicalize_path( char *path ) { gboolean found; g_assert( is_absolute( path ) ); /* Any "/./" can go. */ swap_string( path, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S ); /* Any "//" can go. */ swap_string( path, G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S ); /* Repeatedly search for "/[^/]+/../" and remove it. FIXME ... yuk, should search backwards from the end of the string */ do { char *p; found = FALSE; for( p = path; (p = strchr( p, G_DIR_SEPARATOR )); p++ ) { char *q; q = strchr( p + 1, G_DIR_SEPARATOR ); if( q && is_prefix( G_DIR_SEPARATOR_S "..", q ) ) { memmove( p, q + 3, strlen( q + 3 ) + 1 ); found = TRUE; break; } } } while( found ); /* We may have the empty string ... that's just '/'. */ if( strcmp( path, "" ) == 0 ) strcpy( path, G_DIR_SEPARATOR_S ); } /* Absoluteize a path. Must be FILENAME_MAX chars available. */ void absoluteize_path( char *path ) { if( !is_absolute( path ) ) { char buf[FILENAME_MAX]; char *cwd; im_strncpy( buf, path, FILENAME_MAX ); cwd = g_get_current_dir(); im_snprintf( path, FILENAME_MAX, "%s%s%s", cwd, G_DIR_SEPARATOR_S, buf ); g_free( cwd ); canonicalize_path( path ); } } /* Call a void*-valued function, building a string arg. We expand env. * variables, but that's all. */ void * callv_string( callv_string_fn fn, const char *arg, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; expand_variables( arg, buf ); return( fn( buf, a, b, c ) ); } void * callv_stringva( callv_string_fn fn, const char *fmt, va_list ap, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap ); return( callv_string( fn, buf, a, b, c ) ); } void * callv_stringf( callv_string_fn fn, const char *fmt, ... ) { va_list ap; void *res; va_start( ap, fmt ); res = callv_stringva( fn, fmt, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } /* Call a function, building a filename arg. Nativize and absoluteize too. */ void * callv_string_filename( callv_string_fn fn, const char *filename, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; expand_variables( filename, buf ); nativeize_path( buf ); absoluteize_path( buf ); return( fn( buf, a, b, c ) ); } void * callv_string_filenameva( callv_string_fn fn, const char *fmt, va_list ap, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap ); return( callv_string_filename( fn, buf, a, b, c ) ); } void * callv_string_filenamef( callv_string_fn fn, const char *fmt, ... ) { va_list ap; void *res; va_start( ap, fmt ); res = callv_string_filenameva( fn, fmt, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } /* Call an int-valued function, building a string arg. We expand env. * variables, but that's all. */ int calli_string( calli_string_fn fn, const char *arg, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; expand_variables( arg, buf ); return( fn( buf, a, b, c ) ); } int calli_stringva( calli_string_fn fn, const char *fmt, va_list ap, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap ); return( calli_string( fn, buf, a, b, c ) ); } int calli_stringf( calli_string_fn fn, const char *fmt, ... ) { va_list ap; int res; va_start( ap, fmt ); res = calli_stringva( fn, fmt, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } /* Call a function, building a filename arg. Nativize and absoluteize too. */ int calli_string_filename( calli_string_fn fn, const char *filename, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; expand_variables( filename, buf ); nativeize_path( buf ); absoluteize_path( buf ); return( fn( buf, a, b, c ) ); } int calli_string_filenameva( calli_string_fn fn, const char *fmt, va_list ap, void *a, void *b, void *c ) { char buf[FILENAME_MAX]; (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap ); return( calli_string_filename( fn, buf, a, b, c ) ); } int calli_string_filenamef( calli_string_fn fn, const char *fmt, ... ) { va_list ap; int res; va_start( ap, fmt ); res = calli_string_filenameva( fn, fmt, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } /* Convert a filename to utf8 ... g_free the result. */ char * f2utf8( const char *filename ) { char *utf8; if( !(utf8 = g_filename_to_utf8( filename, -1, NULL, NULL, NULL )) ) utf8 = g_strdup( _( "" ) ); return( utf8 ); } void setenvf( const char *name, const char *fmt, ... ) { va_list ap; char buf[FILENAME_MAX]; va_start( ap, fmt ); (void) im_vsnprintf( buf, FILENAME_MAX, fmt, ap ); va_end( ap ); g_setenv( name, buf, TRUE ); } int check( const char *filename ) { /* Need to work on filenames containing %. */ return( im_existsf( "%s", filename ) ); } /* File exists? */ gboolean existsf( const char *name, ... ) { va_list ap; gboolean res; va_start( ap, name ); res = calli_string_filenameva( (calli_string_fn) check, name, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } int isdir_sub( const char *filename ) { struct stat st; /* Read size and file/dir. */ if( stat( filename, &st ) == -1 ) return( FALSE ); if( !S_ISDIR( st.st_mode ) ) return( FALSE ); return( TRUE ); } gboolean isdir( const char *filename, ... ) { va_list ap; gboolean res; va_start( ap, filename ); res = calli_string_filenameva( (calli_string_fn) isdir_sub, filename, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } static void * mtime_sub( const char *filename, time_t *time ) { struct stat st; if( stat( filename, &st ) == -1 ) return( NULL ); #ifdef HAVE_GETEUID if( st.st_uid != geteuid() ) return( NULL ); #endif /*HAVE_GETEUID*/ *time = st.st_mtime; return( NULL ); } time_t mtime( const char *filename, ... ) { va_list ap; time_t time; time = 0; va_start( ap, filename ); (void) callv_string_filenameva( (callv_string_fn) mtime_sub, filename, ap, &time, NULL, NULL ); va_end( ap ); return( time ); } gboolean mkdirf( const char *name, ... ) { va_list ap; gboolean res; va_start( ap, name ); res = calli_string_filenameva( (calli_string_fn) g_mkdir, name, ap, GINT_TO_POINTER( 0755 ), NULL, NULL ) == 0; va_end( ap ); return( res ); } /* system(), with printf() args and $xxx expansion. */ int systemf( const char *fmt, ... ) { va_list ap; int res; va_start( ap, fmt ); res = calli_stringva( (calli_string_fn) system, fmt, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } gboolean touchf( const char *fmt, ... ) { va_list ap; int fd; va_start( ap, fmt ); fd = calli_string_filenameva( (calli_string_fn) creat, fmt, ap, GINT_TO_POINTER( S_IRUSR | S_IWUSR ), NULL, NULL ); va_end( ap ); (void) close( fd ); return( fd != -1 ); } int unlinkf( const char *fmt, ... ) { va_list ap; int res; va_start( ap, fmt ); res = calli_string_filenameva( (calli_string_fn) unlink, fmt, ap, NULL, NULL, NULL ); va_end( ap ); return( res ); } /* Relative or absolute dir path? Have to expand env vars to see. */ gboolean is_absolute( const char *fname ) { char buf[FILENAME_MAX]; expand_variables( fname, buf ); nativeize_path( buf ); /* We can't use g_path_is_absolute(), we might be given a Windows path * including a drive specifier, and g_path_is_absolute() on unix does * not know about Windows paths. * * We should probably look out for whitespace. */ if( buf[0] == '/' || (buf[0] != '\0' && buf[1] == ':') ) return( TRUE ); else return( FALSE ); } /* OK filename? Ban ':' characters, they may confuse im_open(). Except on * winders :-( */ gboolean is_valid_filename( const char *name ) { const char *p; if( strlen( name ) > FILENAME_MAX ) { error_top( _( "Bad filename." ) ); error_sub( _( "Filename is too long." ) ); return( FALSE ); } if( (p = im_skip_dir( name )) && strspn( p, WHITESPACE ) == strlen( p ) ) { error_top( _( "Bad filename." ) ); error_sub( _( "Filename contains only blank characters." ) ); return( FALSE ); } return( TRUE ); } /* im_strdup(), with NULL supplied. */ char *im_strdupn( const char *txt ) { return( im_strdup( NULL, txt ) ); } /* Free an iOpenFile. */ void ifile_close( iOpenFile *of ) { IM_FREEF( fclose, of->fp ); IM_FREE( of->fname ); IM_FREE( of->fname_real ); IM_FREE( of ); } /* Make an iOpenFile*. */ static iOpenFile * ifile_build( const char *fname ) { iOpenFile *of; if( !(of = INEW( NULL, iOpenFile )) ) return( NULL ); of->fp = NULL; of->fname = NULL; of->fname_real = NULL; of->last_errno = 0; IM_SETSTR( of->fname, fname ); if( !of->fname ) { ifile_close( of ); return( NULL ); } return( of ); } /* Find and open for read. */ iOpenFile * ifile_open_read( const char *name, ... ) { va_list ap; char buf[FILENAME_MAX]; iOpenFile *of; va_start( ap, name ); (void) im_vsnprintf( buf, FILENAME_MAX, name, ap ); va_end( ap ); of = ifile_build( buf ); if( !of ) return( NULL ); if( !(of->fname_real = path_find_file( of->fname )) ) { error_top( _( "Unable to open." ) ); error_sub( _( "Unable to open file \"%s\" for reading.\n%s." ), of->fname, g_strerror( errno ) ); ifile_close( of ); return( NULL ); } if( !(of->fp = (FILE *) callv_string_filename( (callv_string_fn) fopen, of->fname_real, "r", NULL, NULL )) ) { error_top( _( "Unable to open." ) ); error_sub( _( "Unable to open file \"%s\" for reading.\n%s." ), of->fname_real, g_strerror( errno ) ); ifile_close( of ); return( NULL ); } of->read = TRUE; return( of ); } /* Open stdin for read. */ iOpenFile * ifile_open_read_stdin() { iOpenFile *of; if( !(of = ifile_build( "stdin" )) ) return( NULL ); IM_SETSTR( of->fname_real, of->fname ); if( !of->fname_real ) { ifile_close( of ); return( NULL ); } of->fp = stdin; of->read = TRUE; return( of ); } /* Find and open for write. */ iOpenFile * ifile_open_write( const char *name, ... ) { va_list ap; char buf[FILENAME_MAX]; iOpenFile *of; va_start( ap, name ); (void) im_vsnprintf( buf, FILENAME_MAX, name, ap ); va_end( ap ); of = ifile_build( buf ); if( !of ) return( NULL ); IM_SETSTR( of->fname_real, of->fname ); if( !of->fname_real ) { ifile_close( of ); return( NULL ); } if( !(of->fp = (FILE *) callv_string_filename( (callv_string_fn) fopen, of->fname_real, "w", NULL, NULL )) ) { error_top( _( "Unable to open." ) ); error_sub( _( "Unable to open file \"%s\" for writing.\n%s." ), of->fname_real, g_strerror( errno ) ); ifile_close( of ); return( NULL ); } of->read = FALSE; return( of ); } /* fprintf() to a file, checking result. */ gboolean ifile_write( iOpenFile *of, const char *fmt, ... ) { va_list ap; va_start( ap, fmt ); if( vfprintf( of->fp, fmt, ap ) == EOF ) { of->last_errno = errno; error_top( _( "Unable to write." ) ); error_sub( _( "Unable to write to file \"%s\".\n%s." ), of->fname_real, g_strerror( of->last_errno ) ); return( FALSE ); } va_end( ap ); return( TRUE ); } /* Save a string ... if non-NULL. Eg. * fred="boink!" */ gboolean ifile_write_var( iOpenFile *of, const char *name, const char *value ) { if( value ) return( ifile_write( of, " %s=\"%s\"", name, value ) ); return( TRUE ); } /* Load up a file as a string. */ char * ifile_read( iOpenFile *of ) { long len; size_t len2; char *str; /* Find length. */ fseek( of->fp, 0L, 2 ); len = ftell( of->fp ); rewind( of->fp ); if( len < 0 || len > 1024 * 1024 ) { error_top( _( "Unable to read." ) ); error_sub( _( "File \"%s\" too large." ), of->fname_real ); return( NULL ); } /* Allocate memory and fill. */ if( !(str = imalloc( NULL, len + 1 )) ) return( NULL ); /* We can't check len2 against len, since we may be reading a text * file on Windows, in which case this fread will change CRLF to LF * and len2 will be less than len. */ len2 = fread( str, sizeof( char ), (size_t) len, of->fp ); str[len2] = '\0'; return( str ); } /* Load a file into a buffer. Useful for OpenFiles we can't seek in, like * stdin. */ char * ifile_read_buffer( iOpenFile *of, char *buffer, size_t max ) { size_t len; /* -1 off max to leave space for the '\0'. */ len = fread( buffer, sizeof( char ), max - 1, of->fp ); if( !feof( of->fp ) ) { /* File too large for buffer. */ of->last_errno = errno; error_top( _( "Unable to read." ) ); error_sub( _( "Unable to read from file \"%s\".\n%s." ), of->fname_real, g_strerror( of->last_errno ) ); return( NULL ); } buffer[len] = '\0'; return( buffer ); } /* Return '\0' for EOF, -1 for error. */ int ifile_getc( iOpenFile *of ) { int ch; ch = fgetc( of->fp ); if( ch == EOF && feof( of->fp ) ) return( 0 ); else if( ch == EOF ) return( -1 ); else return( ch ); } off_t statf( const char *fmt, ... ) { va_list ap; struct stat st; int result; va_start( ap, fmt ); result = calli_string_filenameva( (calli_string_fn) stat, fmt, ap, &st, NULL, NULL ); va_end( ap ); if( result == -1 || S_ISDIR( st.st_mode ) ) return( 0 ); else return( st.st_size ); } static void * directory_size_sub( const char *filename, double *total ) { *total += statf( "%s", filename ); return( NULL ); } /* Find the amount of 'stuff' in a directory. Result in bytes. Don't look in * sub-dirs. */ double directory_size( const char *dirname ) { double total; total = 0; path_map_dir( dirname, "*", (path_map_fn) directory_size_sub, &total ); return( total ); } /* Escape "%" characters in a string. */ char * escape_percent( const char *in, char *out, int len ) { const char *p; char *q; for( p = in, q = out; *p && q - out < len - 3; p++, q++ ) if( *p == '%' ) { q[0] = '%'; q[1] = '%'; q++; } else *q = *p; *q = '\0'; return( out ); } char * escape_markup( const char *in, char *out, int len ) { const char *p; char *q; for( p = in, q = out; *p && q - out < len - 5; p++, q++ ) if( *p == '<' ) { strcpy( q, "<" ); q += 3; } else if( *p == '>' ) { strcpy( q, ">" ); q += 3; } else if( *p == '&' ) { strcpy( q, "&" ); q += 4; } else *q = *p; *q = '\0'; return( out ); } /* VIPS filenames can have embedded modes. Mode strings are punctuated with * ',' and ':' chars. So strings in modes must have these chars escaped. */ char * escape_mode( const char *in, char *out, int len ) { const char *p; char *q; for( p = in, q = out; *p && q - out < len - 5; p++, q++ ) { if( *p == ':' || *p == ',' ) *q++ = '\\'; *q = *p; } *q = '\0'; return( out ); } /* 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 ) ); } /* Like strtok(), but better. Give a string and a list of break characters; * write a '\0' into the string over the first break character and return a * pointer to the next non-break character. If there are no break characters, * then return a pointer to the end of the string. If passed a pointer to an * empty string or NULL, then return NULL. */ char * 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 ); /* 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 ); } /* Turn a number to a string. 0 is "A", 1 is "B", 25 is "Z", 26 is "AA", 27 is * "AB", etc. */ void number_to_string( int n, char *buf ) { do { int rem = n % 26; *buf++ = (char) (rem + (int) 'A'); n /= 26; } while( n > 0 ); *buf ='\0'; } /* Find the space remaining in a directory, in bytes. A double for >32bit * problem avoidance. <0 for error. */ #ifdef HAVE_SYS_STATVFS_H double find_space( const char *name ) { struct statvfs st; double sz; if( calli_string_filename( (calli_string_fn) statvfs, (gpointer) name, &st, NULL, NULL ) ) /* Set to error value. */ sz = -1; else sz = IM_MAX( 0, (double) st.f_frsize * st.f_bavail ); return( sz ); } #elif (HAVE_SYS_VFS_H || HAVE_SYS_MOUNT_H) double find_space( const char *name ) { struct statfs st; double sz; if( calli_string_filename( (calli_string_fn) statfs, (gpointer) name, &st, NULL, NULL ) ) sz = -1; else sz = IM_MAX( 0, (double) st.f_bsize * st.f_bavail ); return( sz ); } #elif defined OS_WIN32 double find_space( const char *name ) { ULARGE_INTEGER avail; ULARGE_INTEGER total; ULARGE_INTEGER free; double sz; char name2[FILENAME_MAX]; expand_variables( name, name2 ); /* Truncate to just the drive letter. */ if( name2[1] == ':' ) name2[3] = 0; if( !GetDiskFreeSpaceEx( name2, &avail, &total, &free ) ) sz = -1; else sz = IM_MAX( 0, (double) free.QuadPart ); return( sz ); } #else double find_space( const char *name ) { return( -1 ); } #endif /*HAVE_SYS_STATVFS_H*/ /* Make a name for a temp file. Add the specified extension. */ gboolean temp_name( char *name, const char *type ) { /* Some mkstemp() require files to actually exist before they don't * reuse the filename :-( add an extra field. */ static int n = 0; const char *dir; int fd; char buf[FILENAME_MAX]; dir = PATH_TMP; if( !existsf( "%s", dir ) ) dir = G_DIR_SEPARATOR_S; im_snprintf( name, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "untitled-" PACKAGE "-%d-XXXXXXX", dir, n++ ); expand_variables( name, buf ); fd = g_mkstemp( buf ); if( fd == -1 ) { error_top( _( "Unable to create temporary file." ) ); error_sub( _( "Unable to make file \"%s\"\n%s" ), buf, g_strerror( errno ) ); return( FALSE ); } close( fd ); unlinkf( "%s", buf ); im_snprintf( name, FILENAME_MAX, "%s.%s", buf, type ); return( TRUE ); } /* Max/min of an area. */ int findmaxmin( IMAGE *in, int left, int top, int width, int height, double *min, double *max ) { DOUBLEMASK *msk; IMAGE *t1; if( !(t1 = im_open( "temp", "p" )) ) return( -1 ); if( im_extract_area( in, t1, left, top, width, height ) || !(msk = im_stats( t1 )) ) return( -1 ); im_close( t1 ); *min = msk->coeff[0]; *max = msk->coeff[1]; im_free_dmask( msk ); #ifdef DEBUG printf( "findmaxmin: left = %d, top = %d, width = %d, height = %d\n", left, top, width, height ); printf( "findmaxmin: max = %g, min = %g\n", *max, *min ); #endif /*DEBUG*/ return( 0 ); } gboolean char_to_bool( char *str, void *out ) { gboolean *t = (gboolean *) out; if( strcasecmp( "TRUE", str ) == 0 ) *t = TRUE; else *t = FALSE; return( TRUE ); } char * bool_to_char( gboolean b ) { if( b ) return( "true" ); else return( "false" ); } /* Increment a name ... strip any trailing numbers, add one, put numbers back. * Start at 1 if no number there. buf should be at least namelen chars. Keep * leading zeros, if any. */ void increment_name( char *buf ) { char *p; int n; char fmt[256]; /* If there's no number, p will point at the '\0'. */ p = (char *) my_strrspn( buf, NUMERIC ); if( *p ) { n = atoi( p ); im_snprintf( fmt, 256, "%%0%dd", (int) strlen( p ) ); } else { strcpy( fmt, "%d" ); n = 0; } im_snprintf( p, MAX_STRSIZE - (p - buf), fmt, n + 1 ); } /* Increment filename. Eg. "/home/jim/fred_00_tn.tif" becomes * "/home/jim/fred_01_tn.tif" */ void increment_filename( char *filename ) { char buf[FILENAME_MAX]; char suf[FILENAME_MAX]; char tail[FILENAME_MAX]; char *file, *p; im_strncpy( buf, filename, FILENAME_MAX ); /* Save and replace the suffix around an increment_name. */ file = (char *) im_skip_dir( buf ); if( !(p = strrchr( file, '.' )) ) p = file + strlen( file ); im_strncpy( suf, p, FILENAME_MAX ); *p = '\0'; /* Also save any chars to the right of the number component (if any) of * the filename. */ p = (char *) my_strrcspn( file, NUMERIC ); /* No numbers there? Take nothing as the tail and put the number at * the end. */ if( p == file ) p = file + strlen( file ); im_strncpy( tail, p, FILENAME_MAX ); *p = '\0'; increment_name( buf ); strcpy( filename, buf ); strcat( filename, tail ); strcat( filename, suf ); } /* Extract the first line of a string in to buf, extract no more than len * chars. */ int extract_first_line( char *buf, char *str, int len ) { char *p; int n; /* Find next '\n' or '\0'. */ if( (p = strchr( str, '\n' )) ) n = p - str; else n = strlen( str ); n = IM_MIN( len - 1, n ); /* Copy those characters and make sure we have a '\0'. */ strncpy( buf, str, n ); buf[n] = '\0'; return( n ); } /* Make a valid ip name from a filename. */ void name_from_filename( const char *in, char *out ) { const char *p; /* Skip leading path prefix, and any non-alpha. * Don't use isalnum(), since we don't want leading digits. */ p = im_skip_dir( in ); while( *p && !(isalpha( *p ) || *p == '_') ) p += 1; if( !*p ) strcpy( out, "untitled" ); else { char *q; /* Filter non-identifier chars. Stop at the first '.' * character, so we don't get the suffix in there too. */ for( q = out; *p && *p != '.'; p++ ) if( isalnum( *p ) || *p == '_' || *p == '\'' ) *q++ = *p; *q = '\0'; } } /* Do any leak checking we can. */ void util_check_all_destroyed( void ) { if( rect_n_rects ) printf( "rect_n_rects == %d\n", rect_n_rects ); } /* Like im_malloc(), but set our error stuff. */ void * imalloc( IMAGE *im, size_t len ) { void *mem; if( !(mem = im_malloc( im, len )) ) { char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_append_size( &buf, len ); error_top( _( "Out of memory." ) ); error_sub( _( "Request for %s of RAM triggered memory " "allocation failure." ), vips_buf_all( &buf ) ); error_vips(); return( NULL ); } return( mem ); } /* Add a filename to a recent list. If there are more than MAX_RECENT items, * drop the last one off. If this is a dupe, move it to the head of the list. */ GSList * recent_add( GSList *recent, const char *filename ) { char absolute[FILENAME_MAX]; int n; GSList *p; im_strncpy( absolute, filename, FILENAME_MAX ); absoluteize_path( absolute ); for( p = recent; p; p = p->next ) { const char *stored = p->data; if( strcmp( absolute, stored ) == 0 ) { recent = g_slist_remove( recent, stored ); recent = g_slist_prepend( recent, (void *) stored ); return( recent ); } } recent = g_slist_prepend( recent, g_strdup( absolute ) ); if( (n = g_slist_length( recent )) > MAX_RECENT ) { const char *item; item = g_slist_nth_data( recent, n - 1 ); recent = g_slist_remove( recent, item ); g_free( (char *) item ); } return( recent ); } GSList * recent_load( const char *filename ) { iOpenFile *of; GSList *recent; recent = NULL; if( (of = ifile_open_read( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), filename )) ) { char buf[256]; while( fgets( buf, 256, of->fp ) ) { int n; if( (n = strlen( buf )) > 0 ) { if( buf[n - 1] == '\n' ) buf[n - 1] = '\0'; recent = recent_add( recent, buf ); } } ifile_close( of ); } return( recent ); } void recent_free( GSList *recent ) { GSList *p; for( p = recent; p; p = p->next ) { const char *item = (const char *) p->data; g_free( (char *) item ); } g_slist_free( recent ); } static void * recent_save_sub( const char *filename, GSList **old_recent ) { *old_recent = recent_add( *old_recent, filename ); return( NULL ); } static void * recent_save_sub2( const char *filename, iOpenFile *of ) { fprintf( of->fp, "%s\n", filename ); return( NULL ); } void recent_save( GSList *recent, const char *filename ) { iOpenFile *of; GSList *old_recent; /* If there are several nips running, we could be saving over a file * that's been modified since we loaded it. Try to make this less * awful by merging our recent list over the one in the file. */ old_recent = recent_load( filename ); slist_map_rev( recent, (SListMapFn) recent_save_sub, &old_recent ); if( (of = ifile_open_write( "%s" G_DIR_SEPARATOR_S "%s", get_savedir(), filename )) ) { slist_map_rev( old_recent, (SListMapFn) recent_save_sub2, of ); ifile_close( of ); } recent_free( old_recent ); } /* Return the name of the save dir we use ... eg. "$HOME/.nip2-7.10.8", * or maybe "C:\Documents and Settings\john\Application Data" */ const char * get_savedir( void ) { #ifdef OS_WIN32 /* If APPDATA is not defined, default to HOME, we know that will * exist (since we make it if necessary in main()). */ if( g_getenv( "APPDATA" ) && existsf( "%s", g_getenv( "APPDATA" ) ) ) return( "$APPDATA" G_DIR_SEPARATOR_S IP_NAME ); else return( "$HOME" G_DIR_SEPARATOR_S "." IP_NAME ); #elif OS_DARWIN /* Darwin ... in ~/Library */ return( "$HOME" G_DIR_SEPARATOR_S "Library" G_DIR_SEPARATOR_S IP_NAME ); #else /* *nix-style system .. .dot file in home area. */ return( "$HOME" G_DIR_SEPARATOR_S "." IP_NAME ); #endif /*OS_WIN32*/ } /* Turn an slist into a null-terminated array. */ void ** slist_to_array( GSList *list ) { void **array; int i; array = g_new( void *, g_slist_length( list ) + 1 ); for( i = 0; list ; list = list->next, i++ ) array[i] = list->data; array[i] = NULL; return( array ); } /* Length of a NULL-terminated array. */ int array_len( void **array ) { int i; for( i = 0; array[i]; i++ ) ; return( i ); } ================================================ FILE: src/util.h ================================================ /* Declarations for 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* chartype strings. Useful with break_token(). */ #define NUMERIC "0123456789" #define WHITESPACE " \t\r\b\n" /* Like IM_NEW() etc, but set ip's error buffer. */ #define INEW(IM,A) ((A *)imalloc((IM),sizeof(A))) #define IARRAY(IM,N,T) ((T *)imalloc((IM),(N) * sizeof(T))) /* No nulls! Handy for printf() %s */ #define NN( S ) ((S)?(S):"(null)") #define UNREF( X ) do { \ if( X ) { \ g_object_unref( G_OBJECT( X ) ); \ (X) = NULL; \ } \ } while( 0 ) #define GOG_UNREF( X ) do { \ if( X ) { \ gog_object_clear_parent( GOG_OBJECT( X ) ); \ g_object_unref( G_OBJECT( X ) ); \ (X) = NULL; \ } \ } while( 0 ) #define FREESID( SID, OBJ ) do { \ if( (SID) && (OBJ) ) { \ g_signal_handler_disconnect( (OBJ), (SID) ); \ (SID) = 0; \ } \ } while( 0 ) /* Swap two pointers. */ #define SWAPP( A, B ) { \ void *swapp_t; \ \ swapp_t = (A); \ (A) = (B); \ (B) = swapp_t; \ } void vips_buf_appendi( VipsBuf *buf, IMAGE *im ); gboolean vips_buf_appendsc( VipsBuf *buf, gboolean quote, const char *str ); gboolean set_prop( xmlNode *xnode, const char *name, const char *fmt, ... ) __attribute__((format(printf, 3, 4))); gboolean set_sprop( xmlNode *xnode, const char *name, const char *value ); gboolean set_iprop( xmlNode *xnode, const char *name, int value ); gboolean set_dprop( xmlNode *xnode, const char *name, double value ); gboolean set_slprop( xmlNode *xnode, const char *name, GSList *labels ); gboolean set_dlprop( xmlNode *xnode, const char *name, double *values, int n ); gboolean get_sprop( xmlNode *xnode, const char *name, char *buf, int sz ); gboolean get_spropb( xmlNode *xnode, const char *name, VipsBuf *buf ); gboolean get_iprop( xmlNode *xnode, const char *name, int *out ); gboolean get_dprop( xmlNode *xnode, const char *name, double *out ); gboolean get_bprop( xmlNode *xnode, const char *name, gboolean *out ); gboolean get_slprop( xmlNode *xnode, const char *name, GSList **out ); gboolean get_dlprop( xmlNode *xnode, const char *name, double **out ); xmlNode *get_node( xmlNode *base, const char *name ); Rect *rect_dup( Rect *init ); void *rect_free( Rect *rect ); /* Like GFunc, but return a value. */ typedef gpointer (*SListMapFn)( gpointer, gpointer ); typedef gpointer (*SListMap2Fn)( gpointer, gpointer, gpointer ); typedef gpointer (*SListMap3Fn)( gpointer, gpointer, gpointer, gpointer ); typedef gpointer (*SListMap4Fn)( gpointer, gpointer, gpointer, gpointer, gpointer ); typedef gpointer (*SListMap5Fn)( gpointer, gpointer, gpointer, gpointer, gpointer, gpointer ); typedef gpointer (*SListFoldFn)( gpointer, gpointer, gpointer ); typedef gpointer (*SListFold2Fn)( gpointer, gpointer, gpointer, gpointer ); /* Like foreach, but allow abandon. */ void *slist_map( GSList *list, SListMapFn fn, gpointer a ); void *slist_map2( GSList *list, SListMap2Fn fn, gpointer a, gpointer b ); void *slist_map3( GSList *list, SListMap3Fn fn, gpointer a, gpointer b, gpointer c ); void *slist_map4( GSList *list, SListMap4Fn fn, gpointer a, gpointer b, gpointer c, gpointer d ); void *slist_map5( GSList *list, SListMap5Fn fn, gpointer a, gpointer b, gpointer c, gpointer d, gpointer e ); void *slist_map_rev( GSList *list, SListMapFn fn, gpointer a ); void *slist_map2_rev( GSList *list, SListMap2Fn fn, gpointer a, gpointer b ); void *slist_map3_rev( GSList *list, SListMap3Fn fn, void *a, void *b, void *c ); void *map_equal( void *a, void *b ); gboolean slist_equal( GSList *l1, GSList *l2 ); void *slist_fold( GSList *list, void *start, SListFoldFn fn, void *a ); void *slist_fold2( GSList *list, void *start, SListFold2Fn fn, void *a, void *b ); void slist_free_all( GSList *list ); GSList *slist_remove_all( GSList *list, gpointer data ); /* An slist, which tracks the end of the list, for fast append. */ typedef struct _Queue { GSList *list; GSList *tail; int length; } Queue; Queue *queue_new( void ); /* All we need for now. */ void *queue_head( Queue *queue ); void queue_add( Queue *queue, void *data ); gboolean queue_remove( Queue *q, void *data ); int queue_length( Queue *q ); extern VipsBuf error_top_buf; extern VipsBuf error_sub_buf; void error( const char *fmt, ... ) __attribute__((noreturn, format(printf, 1, 2))); void error_block( void ); /* Block updates to error_string */ void error_unblock( void ); void error_clear( void ); void error_top( const char *fmt, ... ) __attribute__((format(printf, 1, 2))); void error_sub( const char *fmt, ... ) __attribute__((format(printf, 1, 2))); void error_vips( void ); void error_vips_all( void ); const char *error_get_top( void ); const char *error_get_sub( void ); gboolean is_postfix( const char *a, const char *b ); gboolean is_prefix( const char *a, const char *b ); gboolean is_caseprefix( const char *a, const char *b ); gboolean is_casepostfix( const char *a, const char *b ); const char *findrightmost( const char *a, const char *b ); char *my_strcasestr( const char *haystack, const char *needle ); void change_suffix( const char *name, char *out, const char *new, const char **olds, int nolds ); char *my_strccpy( char *output, const char *input ); char *my_strecpy( char *output, const char *input, gboolean quote ); const char *my_strrspn( const char *p, const char *spn ); char *trim_nonalpha( char *text ); char *trim_white( char *text ); void *get_element( REGION *ireg, int x, int y, int b ); const char *decode_bandfmt( int f ); const char *decode_type( int t ); void get_image_info( VipsBuf *buf, const char *name ); void expand_variables( const char *in, char *out ); void nativeize_path( char *buf ); void absoluteize_path( char *path ); void canonicalize_path( char *path ); const char *get_vipshome( const char *argv0 ); typedef void *(*callv_string_fn)( const char *name, void *a, void *b, void *c ); void *callv_string( callv_string_fn fn, const char *name, void *a, void *b, void *c ); void *callv_stringva( callv_string_fn fn, const char *name, va_list ap, void *a, void *b, void *c ); void *callv_stringf( callv_string_fn fn, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void *callv_string_filename( callv_string_fn fn, const char *filename, void *a, void *b, void *c ); void *callv_string_filenameva( callv_string_fn fn, const char *name, va_list ap, void *a, void *b, void *c ); void *callv_string_filenamef( callv_string_fn fn, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); typedef int (*calli_string_fn)( const char *name, void *a, void *b, void *c ); int calli_string( calli_string_fn fn, const char *name, void *a, void *b, void *c ); int calli_stringva( calli_string_fn fn, const char *name, va_list ap, void *a, void *b, void *c ); int calli_stringf( calli_string_fn fn, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); int calli_string_filename( calli_string_fn fn, const char *filename, void *a, void *b, void *c ); int calli_string_filenameva( calli_string_fn fn, const char *name, va_list ap, void *a, void *b, void *c ); int calli_string_filenamef( calli_string_fn fn, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); char *f2utf8( const char *filename ); char *im_strdupn( const char *str ); void setenvf( const char *name, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); gboolean existsf( const char *name, ... ) __attribute__((format(printf, 1, 2))); gboolean isdir( const char *filename, ... ) __attribute__((format(printf, 1, 2))); time_t mtime( const char *filename, ... ) __attribute__((format(printf, 1, 2))); gboolean mkdirf( const char *name, ... ) __attribute__((format(printf, 1, 2))); int systemf( const char *fmt, ... ) __attribute__((format(printf, 1, 2))); FILE *popenf( const char *fmt, const char *mode, ... ) __attribute__((format(printf, 1, 3))); gboolean touchf( const char *fmt, ... ) __attribute__((format(printf, 1, 2))); int unlinkf( const char *fmt, ... ) __attribute__((format(printf, 1, 2))); gboolean is_absolute( const char *fname ); gboolean is_valid_filename( const char *name ); /* Text IO to/from files. Track the filename too, to help error messages. */ typedef struct _iOpenFile { FILE *fp; char *fname; /* File we were passed to make this open_file */ char *fname_real; /* File we opened (maybe after search) */ int last_errno; /* On error, last value for errno */ gboolean read; /* True for open read, false for open write */ } iOpenFile; void ifile_close( iOpenFile *of ); iOpenFile *ifile_open_read( const char *name, ... ) __attribute__((format(printf, 1, 2))); iOpenFile *ifile_open_read_stdin(); iOpenFile *ifile_open_write( const char *name, ... ) __attribute__((format(printf, 1, 2))); gboolean ifile_write( iOpenFile *of, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); gboolean ifile_write_var( iOpenFile *of, const char *name, const char *value ); char *ifile_read( iOpenFile *of ); char *ifile_read_buffer( iOpenFile *of, char *buffer, size_t len ); int ifile_getc( iOpenFile *of ); double directory_size( const char *dirname ); char *escape_percent( const char *in, char *out, int len ); char *escape_markup( const char *in, char *out, int len ); char *escape_mode( const char *in, char *out, int len ); char *break_token( char *str, const char *brk ); const char *rpt( char ch, int n ); const char *spc( int n ); void number_to_string( int n, char *buf ); double find_space( const char *name ); gboolean temp_name( char *name, const char *ext ); int findmaxmin( IMAGE *in, int left, int top, int width, int height, double *min, double *max ); gboolean char_to_bool( char *str, void *out ); char *bool_to_char( gboolean b ); void increment_name( char *buf ); void increment_filename( char *filename ); int extract_first_line( char *buf, char *str, int len ); void name_from_filename( const char *in, char *out ); void util_check_all_destroyed( void ); void *imalloc( IMAGE *im, size_t len ); GSList *recent_add( GSList *recent, const char *filename ); GSList *recent_load( const char *filename ); void recent_free( GSList *recent ); void recent_save( GSList *recent, const char *filename ); const char *get_savedir( void ); void **slist_to_array( GSList *list ); int array_len( void **array ); ================================================ FILE: src/value.c ================================================ /* an input value ... put/get methods */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ClassmodelClass *parent_class = NULL; static void value_finalize( GObject *gobject ) { Value *value; #ifdef DEBUG printf( "value_finalize\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_VALUE( gobject ) ); value = VALUE( gobject ); /* My instance finalize stuff. */ vips_buf_destroy( &value->caption_buffer ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Default caption: just "class-name class.value". */ static const char * value_generate_caption( iObject *iobject ) { Value *value = VALUE( iobject ); ValueClass *value_class = VALUE_GET_CLASS( value ); VipsBuf *buf = &value->caption_buffer; vips_buf_rewind( buf ); if( !heapmodel_name( HEAPMODEL( value ), buf ) ) vips_buf_appends( buf, G_OBJECT_CLASS_NAME( value_class ) ); vips_buf_appends( buf, " " ); heapmodel_value( HEAPMODEL( value ), buf ); return( vips_buf_all( buf ) ); } static View * value_view_new( Model *model, View *parent ) { return( valueview_new() ); } static void value_class_init( ValueClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ gobject_class->finalize = value_finalize; iobject_class->generate_caption = value_generate_caption; model_class->view_new = value_view_new; } static void value_init( Value *value ) { vips_buf_init_dynamic( &value->caption_buffer, MAX_LINELENGTH ); } GType value_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( ValueClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) value_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Value ), 32, /* n_preallocs */ (GInstanceInitFunc) value_init, }; type = g_type_register_static( TYPE_CLASSMODEL, "Value", &info, 0 ); } return( type ); } ================================================ FILE: src/value.h ================================================ /* abstract base class for real/group/vector */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_VALUE (value_get_type()) #define VALUE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_VALUE, Value )) #define VALUE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_VALUE, ValueClass)) #define IS_VALUE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_VALUE )) #define IS_VALUE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_VALUE )) #define VALUE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_VALUE, ValueClass )) typedef struct _Value { Classmodel model; /* Build caption buffer here. */ VipsBuf caption_buffer; } Value; typedef struct _ValueClass { ClassmodelClass parent_class; /* My methods. */ } ValueClass; GType value_get_type( void ); ================================================ FILE: src/valueview.c ================================================ /* display a minimal graphic for an object (just the caption) */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static GraphicviewClass *parent_class = NULL; static void valueview_refresh( vObject *vobject ) { Valueview *valueview = VALUEVIEW( vobject ); Model *model = MODEL( vobject->iobject ); #ifdef DEBUG printf( "valueview_refresh: " ); row_name_print( HEAPMODEL( model )->row ); printf( "\n" ); #endif /*DEBUG*/ set_gcaption( valueview->label, "%s", NN( IOBJECT( model )->caption ) ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void valueview_link( View *view, Model *model, View *parent ) { Valueview *valueview = VALUEVIEW( view ); Rowview *rview = ROWVIEW( parent->parent ); VIEW_CLASS( parent_class )->link( view, model, parent ); (void) rowview_menu_attach( rview, valueview->eb ); } static void valueview_class_init( ValueviewClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ vobject_class->refresh = valueview_refresh; view_class->link = valueview_link; } static gboolean valueview_event_cb( GtkWidget *widget, GdkEvent *ev, Valueview *valueview ) { Model *model = MODEL( VOBJECT( valueview )->iobject ); Row *row = HEAPMODEL( model )->row; gboolean handled = FALSE; switch( ev->type ) { case GDK_BUTTON_PRESS: if( ev->button.button == 1 ) { row_select_modifier( row, ev->button.state ); handled = TRUE; } break; case GDK_2BUTTON_PRESS: if( ev->button.button == 1 ) { model_edit( widget, MODEL( model ) ); handled = TRUE; } break; default: break; } return( handled ); } static void valueview_init( Valueview *valueview ) { #ifdef DEBUG printf( "valueview_init\n" ); #endif /*DEBUG*/ valueview->eb = gtk_event_box_new(); gtk_widget_add_events( GTK_WIDGET( valueview->eb ), GDK_POINTER_MOTION_HINT_MASK ); gtk_box_pack_start( GTK_BOX( valueview ), valueview->eb, FALSE, FALSE, 0 ); valueview->label = gtk_label_new( "" ); gtk_misc_set_alignment( GTK_MISC( valueview->label ), 0, 0.5 ); gtk_misc_set_padding( GTK_MISC( valueview->label ), 2, 0 ); gtk_container_add( GTK_CONTAINER( valueview->eb ), valueview->label ); gtk_widget_set_name( valueview->eb, "caption_widget" ); gtk_signal_connect( GTK_OBJECT( valueview->eb ), "event", GTK_SIGNAL_FUNC( valueview_event_cb ), valueview ); gtk_widget_show_all( GTK_WIDGET( valueview->eb ) ); } GtkType valueview_get_type( void ) { static GtkType valueview_type = 0; if( !valueview_type ) { static const GtkTypeInfo info = { "Valueview", sizeof( Valueview ), sizeof( ValueviewClass ), (GtkClassInitFunc) valueview_class_init, (GtkObjectInitFunc) valueview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; valueview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info ); } return( valueview_type ); } View * valueview_new( void ) { Valueview *valueview = gtk_type_new( TYPE_VALUEVIEW ); return( VIEW( valueview ) ); } ================================================ FILE: src/valueview.h ================================================ /* a basic view of a model ... just show the caption */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_VALUEVIEW (valueview_get_type()) #define VALUEVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_VALUEVIEW, Valueview )) #define VALUEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_VALUEVIEW, ValueviewClass )) #define IS_VALUEVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_VALUEVIEW )) #define IS_VALUEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_VALUEVIEW )) typedef struct _Valueview { Graphicview parent_object; GtkWidget *eb; GtkWidget *label; } Valueview; typedef struct _ValueviewClass { GraphicviewClass parent_class; /* My methods. */ } ValueviewClass; GtkType valueview_get_type( void ); View *valueview_new( void ); ================================================ FILE: src/vector.c ================================================ /* display a vector */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ValueClass *parent_class = NULL; static void vector_class_init( VectorClass *class ) { parent_class = g_type_class_peek_parent( class ); /* Create signals. */ model_register_loadable( MODEL_CLASS( class ) ); } static void vector_init( Vector *vector ) { iobject_set( IOBJECT( vector ), CLASS_VECTOR, NULL ); } GType vector_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( VectorClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) vector_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Vector ), 32, /* n_preallocs */ (GInstanceInitFunc) vector_init, }; type = g_type_register_static( TYPE_VALUE, "Vector", &info, 0 ); } return( type ); } ================================================ FILE: src/vector.h ================================================ /* a vector in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_VECTOR (vector_get_type()) #define VECTOR( obj ) (GTK_CHECK_CAST( (obj), TYPE_VECTOR, Vector )) #define VECTOR_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_VECTOR, VectorClass )) #define IS_VECTOR( obj ) (GTK_CHECK_TYPE( (obj), TYPE_VECTOR )) #define IS_VECTOR_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_VECTOR )) typedef struct _Vector { Value parent_object; } Vector; typedef struct _VectorClass { ValueClass parent_class; /* My methods. */ } VectorClass; GType vector_get_type( void ); ================================================ FILE: src/view.c ================================================ /* abstract base class for items which can form a row in a tally */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG #define DEBUG_VIEWCHILD */ /* Time each refresh #define DEBUG_TIME */ #include "ip.h" static vObjectClass *parent_class = NULL; static GSList *view_scannable = NULL; static GSList *view_resettable = NULL; void view_scannable_register( View *view ) { /* Must have a scan method. */ g_assert( VIEW_GET_CLASS( view )->scan ); if( !view->scannable ) { view_scannable = g_slist_prepend( view_scannable, view ); view->scannable = TRUE; } } void view_scannable_unregister( View *view ) { if( view->scannable ) { view_scannable = g_slist_remove( view_scannable, view ); view->scannable = FALSE; } } gboolean view_scan_all( void ) { if( slist_map( view_scannable, (SListMapFn) view_scan, NULL ) ) return( FALSE ); view_reset_all(); return( TRUE ); } void view_resettable_register( View *view ) { /* Must have a reset method. */ g_assert( VIEW_GET_CLASS( view )->reset ); if( !view->resettable ) { view_resettable = g_slist_prepend( view_resettable, view ); view->resettable = TRUE; } } void view_resettable_unregister( View *view ) { if( view->resettable ) { view_resettable = g_slist_remove( view_resettable, view ); view->resettable = FALSE; } } void view_reset_all( void ) { (void) slist_map( view_resettable, (SListMapFn) view_reset, NULL ); } /* Should a viewchild be displayed? If model->display is true, also give the * enclosing view a chance to filter. */ static gboolean view_viewchild_display( ViewChild *viewchild ) { View *parent_view = viewchild->parent_view; Model *child_model = viewchild->child_model; ViewClass *parent_view_class = VIEW_GET_CLASS( parent_view ); if( child_model->display && parent_view_class->display ) return( parent_view_class->display( parent_view, child_model ) ); return( child_model->display ); } /* One of the children of the model we watch has changed ... create or destroy * the child view as required. */ static void view_viewchild_changed( Model *model, ViewChild *viewchild ) { gboolean display = view_viewchild_display( viewchild ); View *child = viewchild->child_view; if( !display && child ) { #ifdef DEBUG_VIEWCHILD printf( "view_viewchild_changed: %s \"%s\", removing view\n", G_OBJECT_TYPE_NAME( model ), NN( IOBJECT( model )->name ) ); printf( "view_viewchild_changed: %s\n", G_OBJECT_TYPE_NAME( child ) ); #endif /*DEBUG_VIEWCHILD*/ DESTROY_GTK( child ); } else if( display && !child ) { #ifdef DEBUG_VIEWCHILD printf( "view_viewchild_changed: %s \"%s\", adding view\n", G_OBJECT_TYPE_NAME( model ), NN( IOBJECT( model )->name ) ); #endif /*DEBUG_VIEWCHILD*/ model_view_new( model, viewchild->parent_view ); } } static ViewChild * view_viewchild_new( View *parent_view, Model *child_model ) { ViewChild *viewchild; #ifdef DEBUG_VIEWCHILD printf( "view_viewchild_new: view \"%s\" watching %s \"%s\"\n", G_OBJECT_TYPE_NAME( parent_view ), G_OBJECT_TYPE_NAME( child_model ), NN( IOBJECT( child_model )->name ) ); #endif /*DEBUG_VIEWCHILD*/ if( !(viewchild = INEW( NULL, ViewChild )) ) return( NULL ); viewchild->parent_view = parent_view; viewchild->child_model = child_model; viewchild->child_model_changed_sid = g_signal_connect( child_model, "changed", G_CALLBACK( view_viewchild_changed ), viewchild ); viewchild->child_view = NULL; parent_view->managed = g_slist_append( parent_view->managed, viewchild ); return( viewchild ); } static void * view_viewchild_destroy( ViewChild *viewchild ) { View *parent_view = viewchild->parent_view; View *child_view = viewchild->child_view; #ifdef DEBUG_VIEWCHILD printf( "view_viewchild_destroy: view %s watching model %s\n", G_OBJECT_TYPE_NAME( viewchild->parent_view ), G_OBJECT_TYPE_NAME( viewchild->child_model ) ); #endif /*DEBUG_VIEWCHILD*/ if( child_view ) { g_assert( child_view->parent == parent_view ); child_view->parent = NULL; } FREESID( viewchild->child_model_changed_sid, viewchild->child_model ); parent_view->managed = g_slist_remove( parent_view->managed, viewchild ); im_free( viewchild ); return( NULL ); } static void * view_viewchild_test_child_model( ViewChild *viewchild, Model *child_model ) { #ifdef DEBUG printf( "view_viewchild_test_child_model: model %s \"%s\"\n", G_OBJECT_TYPE_NAME( child_model ), NN( IOBJECT( child_model )->name ) ); #endif /*DEBUG*/ if( viewchild->child_model == child_model ) return( viewchild ); return( NULL ); } /* Do we have a model? */ gboolean view_hasmodel( View *view ) { return( VOBJECT( view )->iobject != NULL ); } void * view_model_test( View *view, Model *model ) { if( VOBJECT( view )->iobject == IOBJECT( model ) ) return( view ); return( NULL ); } /* Link to enclosing model and view. */ void view_link( View *view, Model *model, View *parent ) { VIEW_GET_CLASS( view )->link( view, model, parent ); } /* Add a child. */ void view_child_add( View *parent, View *child ) { VIEW_GET_CLASS( parent )->child_add( parent, child ); } /* Remove a child. */ void view_child_remove( View *child ) { View *parent = child->parent; VIEW_GET_CLASS( parent )->child_remove( parent, child ); } /* Child needs repositioning. */ void view_child_position( View *child ) { View *parent = child->parent; VIEW_GET_CLASS( parent )->child_position( parent, child ); } /* Pop child to front of stacking order. */ void view_child_front( View *child ) { View *parent = child->parent; if( parent ) VIEW_GET_CLASS( parent )->child_front( parent, child ); } /* Break link to model. */ void view_unlink( View *view ) { g_assert( view != NULL ); g_assert( VOBJECT( view )->iobject != NULL ); g_assert( IS_VIEW( view ) && IS_MODEL( VOBJECT( view )->iobject ) ); FREESID( view->pos_changed_sid, VOBJECT( view )->iobject ); FREESID( view->scrollto_sid, VOBJECT( view )->iobject ); FREESID( view->layout_sid, VOBJECT( view )->iobject ); FREESID( view->reset_sid, VOBJECT( view )->iobject ); FREESID( view->front_sid, VOBJECT( view )->iobject ); FREESID( view->child_add_sid, VOBJECT( view )->iobject ); FREESID( view->child_remove_sid, VOBJECT( view )->iobject ); FREESID( view->child_detach_sid, VOBJECT( view )->iobject ); FREESID( view->child_attach_sid, VOBJECT( view )->iobject ); } static void view_destroy( GtkObject *object ) { View *view; g_return_if_fail( object != NULL ); g_return_if_fail( IS_VIEW( object ) ); view = VIEW( object ); #ifdef DEBUG printf( "view_destroy: \"%s\"\n", G_OBJECT_TYPE_NAME( object ) ); #endif /*DEBUG*/ /* We're probably changing the size of our enclosing column. */ view_resize( view ); if( view->scannable ) view_scannable_unregister( view ); if( view->resettable ) view_resettable_unregister( view ); if( VOBJECT( view )->iobject ) view_unlink( view ); if( view->parent ) view_child_remove( view ); slist_map( view->managed, (SListMapFn) view_viewchild_destroy, NULL ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void view_finalize( GObject *gobject ) { #ifdef DEBUG printf( "view_finalize: \"%s\"\n", G_OBJECT_TYPE_NAME( gobject ) ); #endif /*DEBUG*/ G_OBJECT_CLASS( parent_class )->finalize( gobject ); } /* Called for model pos_changed signal ... queue a refresh. */ static void view_model_pos_changed( Model *model, View *view ) { g_assert( IS_MODEL( model ) ); g_assert( IS_VIEW( view ) ); #ifdef DEBUG printf( "view_model_pos_changed: %s %s \"%s\"\n", G_OBJECT_TYPE_NAME( view ), G_OBJECT_TYPE_NAME( model ), NN( IOBJECT( model )->name ) ); #endif /*DEBUG*/ vobject_refresh_queue( VOBJECT( view ) ); } /* Called for model scrollto signal ... try scrolling. */ static void view_model_scrollto( Model *model, ModelScrollPosition position, View *view ) { g_assert( IS_MODEL( model ) ); g_assert( IS_VIEW( view ) ); #ifdef DEBUG printf( "view_model_scrollto: %s\n", IOBJECT( model )->name ); #endif /*DEBUG*/ view_scrollto( view, position ); } /* Called for model layout signal ... try to lay out children. */ static void view_model_layout( Model *model, View *view ) { g_assert( IS_MODEL( model ) ); g_assert( IS_VIEW( view ) ); #ifdef DEBUG printf( "view_model_layout: %s\n", IOBJECT( model )->name ); #endif /*DEBUG*/ view_layout( view ); } /* Called for model reset signal ... try resetting. */ static void view_model_reset( Model *model, View *view ) { g_assert( IS_MODEL( model ) ); g_assert( IS_VIEW( view ) ); #ifdef DEBUG printf( "view_model_reset: %s\n", IOBJECT( model )->name ); #endif /*DEBUG*/ view_reset( view ); } /* Called for model front signal ... bring view to front. */ static void view_model_front( Model *model, View *view ) { g_assert( IS_MODEL( model ) ); g_assert( IS_VIEW( view ) ); #ifdef DEBUG printf( "view_model_front: model %s \"%s\"\n", G_OBJECT_TYPE_NAME( model ), NN( IOBJECT( model )->name ) ); printf( "\tview %s\n", G_OBJECT_TYPE_NAME( view ) ); #endif /*DEBUG*/ view_child_front( view ); } /* Called for model child_add signal ... start watching that child. */ static void view_model_child_add( Model *parent, Model *child, int pos, View *parent_view ) { ViewChild *viewchild; #ifdef DEBUG printf( "view_model_child_add: parent %s \"%s\"\n", G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) ); #endif /*DEBUG*/ g_assert( IS_MODEL( parent ) ); g_assert( IS_MODEL( child ) ); g_assert( IS_VIEW( parent_view ) ); #ifdef DEBUG viewchild = slist_map( parent_view->managed, (SListMapFn) view_viewchild_test_child_model, child ); g_assert( !viewchild ); #endif /*DEBUG*/ viewchild = view_viewchild_new( parent_view, child ); view_viewchild_changed( child, viewchild ); } /* Called for model child_remove signal ... stop watching that child. child * may have been finalized already. */ static void view_model_child_remove( iContainer *parent, iContainer *child, View *parent_view ) { ViewChild *viewchild; #ifdef DEBUG { printf( "view_model_child_remove: child %s \"%s\"; " "parent %s \"%s\"\n", G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ), G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) ); printf( "view_model_child_remove: parent_view = view of %s \"%s\"\n", G_OBJECT_TYPE_NAME( VOBJECT( parent_view )->iobject ), NN( IOBJECT( VOBJECT( parent_view )->iobject )->name ) ); } #endif /*DEBUG*/ viewchild = slist_map( parent_view->managed, (SListMapFn) view_viewchild_test_child_model, child ); g_assert( viewchild ); (void) view_viewchild_destroy( viewchild ); } /* Called for model parent_detach signal ... remove the viewchild for this * child. child_attach will build a new one. */ static void view_model_child_detach( iContainer *old_parent, iContainer *child, View *old_parent_view ) { ViewChild *viewchild; #ifdef DEBUG { printf( "view_model_child_detach: child %s \"%s\"; " "old_parent %s \"%s\"\n", G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ), G_OBJECT_TYPE_NAME( old_parent ), NN( IOBJECT( old_parent )->name ) ); printf( "view_model_child_detach: old_parent_view = " "view of %s \"%s\"\n", G_OBJECT_TYPE_NAME( VOBJECT( old_parent_view )->iobject ), NN( IOBJECT( VOBJECT( old_parent_view )->iobject )->name ) ); } #endif /*DEBUG*/ viewchild = slist_map( old_parent_view->managed, (SListMapFn) view_viewchild_test_child_model, child ); g_assert( viewchild ); g_assert( !child->temp_view ); child->temp_view = viewchild->child_view; (void) view_viewchild_destroy( viewchild ); } /* Called for model child_attach signal ... make a new viewchild on the new * parent view. */ static void view_model_child_attach( iContainer *new_parent, iContainer *child, int pos, View *new_parent_view ) { ViewChild *viewchild; g_assert( !slist_map( new_parent_view->managed, (SListMapFn) view_viewchild_test_child_model, child ) ); viewchild = view_viewchild_new( new_parent_view, MODEL( child ) ); g_assert( child->temp_view && IS_VIEW( child->temp_view ) ); viewchild->child_view = child->temp_view; child->temp_view = NULL; viewchild->child_view->parent = new_parent_view; } static void * view_real_link_sub( Model *child_model, View *parent_view ) { ViewChild *viewchild; viewchild = view_viewchild_new( parent_view, child_model ); view_viewchild_changed( child_model, viewchild ); return( NULL ); } /* Link to model and to enclosing view. */ static void view_real_link( View *view, Model *model, View *parent_view ) { g_assert( view != NULL ); g_assert( IS_VIEW( view ) && IS_MODEL( model ) ); g_assert( !VOBJECT( view )->iobject ); #ifdef DEBUG printf( "view_real_link: linking %s to model %s \"%s\"\n", G_OBJECT_TYPE_NAME( view ), G_OBJECT_TYPE_NAME( model ), NN( IOBJECT( model )->name ) ); #endif /*DEBUG*/ vobject_link( VOBJECT( view ), IOBJECT( model ) ); if( parent_view ) view_child_add( parent_view, view ); view->pos_changed_sid = g_signal_connect( model, "pos_changed", G_CALLBACK( view_model_pos_changed ), view ); view->scrollto_sid = g_signal_connect( model, "scrollto", G_CALLBACK( view_model_scrollto ), view ); view->layout_sid = g_signal_connect( model, "layout", G_CALLBACK( view_model_layout ), view ); view->reset_sid = g_signal_connect( model, "reset", G_CALLBACK( view_model_reset ), view ); view->front_sid = g_signal_connect( model, "front", G_CALLBACK( view_model_front ), view ); view->child_add_sid = g_signal_connect( model, "child_add", G_CALLBACK( view_model_child_add ), view ); view->child_remove_sid = g_signal_connect( model, "child_remove", G_CALLBACK( view_model_child_remove ), view ); view->child_detach_sid = g_signal_connect( model, "child_detach", G_CALLBACK( view_model_child_detach ), view ); view->child_attach_sid = g_signal_connect( model, "child_attach", G_CALLBACK( view_model_child_attach ), view ); icontainer_map( ICONTAINER( model ), (icontainer_map_fn) view_real_link_sub, view, NULL ); gtk_widget_show( GTK_WIDGET( view ) ); } static void view_real_child_add( View *parent, View *child ) { ViewChild *viewchild; g_assert( IS_VIEW( parent ) && IS_VIEW( child ) ); g_assert( child->parent == NULL ); #ifdef DEBUG printf( "view_real_child_add: parent %p %s, child %p %s\n", parent, G_OBJECT_TYPE_NAME( parent ), child, G_OBJECT_TYPE_NAME( child ) ); #endif /*DEBUG*/ viewchild = slist_map( parent->managed, (SListMapFn) view_viewchild_test_child_model, VOBJECT( child)->iobject ); g_assert( viewchild ); g_assert( viewchild->child_view == NULL ); /* Not all views are true widgets (ie. get _ref()'s and _sink()'d by a * parent in gtk_container()). Ref and sink ourselves to ensure that * even these odd views get unfloated. See also * view_real_child_remove(). Affects the tool/toolkit views, and * rowview at least. */ child->parent = parent; viewchild->child_view = child; g_object_ref( GTK_OBJECT( child ) ); gtk_object_sink( GTK_OBJECT( child ) ); } static void view_real_child_remove( View *parent, View *child ) { ViewChild *viewchild; #ifdef DEBUG printf( "view_real_child_remove: parent %s, child %s\n", G_OBJECT_TYPE_NAME( parent ), G_OBJECT_TYPE_NAME( child ) ); #endif /*DEBUG*/ viewchild = slist_map( parent->managed, (SListMapFn) view_viewchild_test_child_model, VOBJECT( child )->iobject ); /* Can have floating views which are not part of the viewchild system. */ if( viewchild && viewchild->child_view == child ) { viewchild->child_view = NULL; g_object_unref( G_OBJECT( child ) ); } child->parent = NULL; } static void view_real_child_position( View *parent, View *child ) { } static void view_real_child_front( View *parent, View *child ) { } static void view_real_reset( View *view ) { view_resettable_unregister( view ); } static void * view_real_scan( View *view ) { Model *model = MODEL( VOBJECT( view )->iobject ); Heapmodel *heapmodel; view_scannable_unregister( view ); /* If we've changed something in this model, mark it for recomp. */ if( model && IS_HEAPMODEL( model ) && (heapmodel = HEAPMODEL( model ))->modified && heapmodel->row ) { Expr *expr = heapmodel->row->expr; if( expr ) (void) expr_dirty( expr, link_serial_new() ); } return( NULL ); } static void view_class_init( ViewClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); GtkObjectClass *object_class = (GtkObjectClass*) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = view_finalize; object_class->destroy = view_destroy; /* Create signals. */ /* Init default methods. */ class->link = view_real_link; class->child_add = view_real_child_add; class->child_remove = view_real_child_remove; class->child_position = view_real_child_position; class->child_front = view_real_child_front; class->display = NULL; class->reset = view_real_reset; class->scan = view_real_scan; class->scrollto = NULL; class->layout = NULL; } static void view_init( View *view ) { /* Init our instance fields. */ view->pos_changed_sid = 0; view->scrollto_sid = 0; view->layout_sid = 0; view->reset_sid = 0; view->front_sid = 0; view->child_add_sid = 0; view->child_remove_sid = 0; view->child_detach_sid = 0; view->child_attach_sid = 0; view->parent = NULL; view->scannable = FALSE; view->resettable = FALSE; } GtkType view_get_type( void ) { static GtkType view_type = 0; if( !view_type ) { static const GtkTypeInfo view_info = { "View", sizeof( View ), sizeof( ViewClass ), (GtkClassInitFunc) view_class_init, (GtkObjectInitFunc) view_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; view_type = gtk_type_unique( TYPE_VOBJECT, &view_info ); } return( view_type ); } /* Trigger the reset method for a view. */ void * view_reset( View *view ) { ViewClass *view_class = VIEW_GET_CLASS( view ); if( view_class->reset ) view_class->reset( view ); return( NULL ); } /* Trigger the scan method for a view. */ void * view_scan( View *view ) { ViewClass *view_class = VIEW_GET_CLASS( view ); if( view_class->scan ) return( view_class->scan( view ) ); return( NULL ); } /* Trigger the scrollto method for a view. */ void * view_scrollto( View *view, ModelScrollPosition position ) { ViewClass *view_class = VIEW_GET_CLASS( view ); if( view_class->scrollto ) view_class->scrollto( view, position ); return( NULL ); } /* Trigger the layout method for a view. */ void * view_layout( View *view ) { ViewClass *view_class = VIEW_GET_CLASS( view ); if( view_class->layout ) view_class->layout( view ); return( NULL ); } static void * view_map_sub( ViewChild *viewchild, view_map_fn fn, void *a, void *b ) { if( viewchild->child_view ) return( fn( viewchild->child_view, a, b ) ); return( NULL ); } /* Map over a view's children. */ void * view_map( View *view, view_map_fn fn, void *a, void *b ) { return( slist_map3( view->managed, (SListMap3Fn) view_map_sub, (void *) fn, a, b ) ); } /* Apply a function to view, and to all it's children. */ void * view_map_all( View *view, view_map_fn fn, void *a ) { View *result; if( (result = fn( view, a, NULL )) ) return( result ); return( view_map( view, (view_map_fn) view_map_all, (void *) fn, a ) ); } void view_save_as_cb( GtkWidget *menu, GtkWidget *host, View *view ) { Model *model = MODEL( VOBJECT( view )->iobject ); if( IS_FILEMODEL( model ) ) { iWindow *iwnd = IWINDOW( view_get_toplevel( view ) ); filemodel_inter_saveas( iwnd, FILEMODEL( model ) ); } } void view_save_cb( GtkWidget *menu, GtkWidget *host, View *view ) { Model *model = MODEL( VOBJECT( view )->iobject ); if( IS_FILEMODEL( model ) ) { iWindow *iwnd = IWINDOW( view_get_toplevel( view ) ); filemodel_inter_save( iwnd, FILEMODEL( model ) ); } } void view_close_cb( GtkWidget *menu, GtkWidget *host, View *view ) { Model *model = MODEL( VOBJECT( view )->iobject ); if( IS_FILEMODEL( model ) ) { iWindow *iwnd = IWINDOW( view_get_toplevel( view ) ); filemodel_inter_savenclose( iwnd, FILEMODEL( model ) ); } } /* Callback for "activate" on a view. */ void view_activate_cb( View *view ) { view_scannable_register( view ); symbol_recalculate_all(); } /* Callback for "changed" on a view. */ void view_changed_cb( View *view ) { /* Make sure it's on the scannable list. */ view_scannable_register( view ); } void view_not_implemented_cb( GtkWidget *menu, GtkWidget *host, View *view ) { error_top( _( "Not implemented." ) ); iwindow_alert( GTK_WIDGET( view ), GTK_MESSAGE_ERROR ); } GtkWidget * view_get_toplevel( View *view ) { while( IS_VIEW( view ) && view->parent ) view = view->parent; return( gtk_widget_get_toplevel( GTK_WIDGET( view ) ) ); } Columnview * view_get_columnview( View *child ) { View *view; for( view = child; view && !IS_COLUMNVIEW( view ); view = view->parent ) ; if( !view ) return( NULL ); return( COLUMNVIEW( view ) ); } /* A view has changed size ... rethink the enclosing column geo. Helps table * to not break. */ void * view_resize( View *view ) { Columnview *cview = view_get_columnview( view ); if( cview ) gtk_widget_queue_resize( GTK_WIDGET( cview ) ); return( NULL ); } ================================================ FILE: src/view.h ================================================ /* abstract base class for our UI widgets */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_VIEW (view_get_type()) #define VIEW( obj ) (GTK_CHECK_CAST( (obj), TYPE_VIEW, View )) #define VIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_VIEW, ViewClass )) #define IS_VIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_VIEW )) #define IS_VIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_VIEW )) #define VIEW_GET_CLASS( obj ) \ (GTK_CHECK_GET_CLASS( (obj), TYPE_VIEW, ViewClass )) /* We track all of the children of our model, listening to "changed", so we * can lazily add or remove child views of us as the model requests. */ typedef struct { View *parent_view; /* Us */ Model *child_model; /* The child we are watching */ guint child_model_changed_sid; /* Listen to "changed" on child here */ View *child_view; /* The child view for this model */ } ViewChild; struct _View { vObject parent_object; /* My instance vars. */ guint pos_changed_sid; /* Signals we use to watch iObject */ guint scrollto_sid; guint layout_sid; guint front_sid; guint reset_sid; guint child_add_sid; guint child_remove_sid; guint child_detach_sid; guint child_attach_sid; View *parent; /* Enclosing view (if any) */ GSList *managed; /* List of ViewChild for us */ gboolean scannable; /* On scannable list */ gboolean resettable; /* On resettable list */ }; typedef struct _ViewClass { vObjectClass parent_class; /* Create/destroy link this view is about to be linked to this model with this parent view child_add this view has just gained a child child_remove this view is about to lose a child child_position this child needs repositioning child_front pop this child to the front display should this child be displayed */ void (*link)( View *, Model *, View * ); void (*child_add)( View *parent, View *child ); void (*child_remove)( View *parent, View *child ); void (*child_position)( View *parent, View *child ); void (*child_front)( View *parent, View *child ); gboolean (*display)( View *parent, Model *child ); /* State change reset reset edit mode ... eg. text pops back to value display scan scan widgets, reading any new text off the display scrollto try to make this view visible layout try to lay children out */ void (*reset)( View * ); void *(*scan)( View * ); void (*scrollto)( View *, ModelScrollPosition ); void (*layout)( View * ); } ViewClass; void view_scannable_register( View *view ); void view_scannable_unregister( View *view ); gboolean view_scan_all( void ); void view_resettable_register( View *view ); void view_resettable_unregister( View *view ); void view_reset_all( void ); gboolean view_hasmodel( View *view ); void *view_model_test( View *child, Model *model ); GtkType view_get_type( void ); void view_link( View *view, Model *model, View *parent ); void view_unlink( View *view ); void view_child_add( View *parent, View *child ); void view_child_remove( View *child ); void view_child_position( View *child ); void view_child_front( View *child ); void *view_reset( View *view ); void *view_scan( View *view ); void *view_scrollto( View *view, ModelScrollPosition ); void *view_layout( View *view ); void *view_map( View *view, view_map_fn fn, void *a, void *b ); void *view_map_all( View *view, view_map_fn fn, void *a ); void view_save_as_cb( GtkWidget *menu, GtkWidget *host, View *view ); void view_save_cb( GtkWidget *menu, GtkWidget *host, View *view ); void view_close_cb( GtkWidget *menu, GtkWidget *host, View *view ); void view_activate_cb( View *view ); void view_changed_cb( View *view ); void view_not_implemented_cb( GtkWidget *menu, GtkWidget *host, View *view ); GtkWidget *view_get_toplevel( View *view ); Columnview *view_get_columnview( View *child ); void *view_resize( View *view ); ================================================ FILE: src/vipsobject.c ================================================ /* Interface to VipsObject. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ /* Maxiumum number of args to constructor. */ #define MAX_VIPS_ARGS (100) /* What we track during construct. */ typedef struct _Vo { Reduce *rc; /* Object we are building. */ VipsObject *object; const char *name; /* Required args supplied to us from nip. */ PElement args[MAX_VIPS_ARGS]; int nargs_supplied; /* Number of required input args the object has. */ int nargs_required; /* Number of output args the object has. */ int nargs_output; /* A place to build the output, safe from the GC. */ Element out; } Vo; static void vo_free( Vo *vo ) { heap_unregister_element( vo->rc->heap, &vo->out ); VIPS_UNREF( vo->object ); im_free( vo ); } static Vo * vo_new( Reduce *rc, const char *name ) { const VipsObjectClass *class; Vo *vo; if( !(class = vips_class_find( "VipsObject", name )) ) return( NULL ); if( !(vo = INEW( NULL, Vo )) ) return( NULL ); vo->rc = rc; vo->object = g_object_new( G_OBJECT_CLASS_TYPE( class ), NULL ); vo->name = class->nickname; vo->nargs_supplied = 0; vo->nargs_required = 0; vo->nargs_output = 0; vo->out.type = ELEMENT_NOVAL; vo->out.ele = (void *) 12; heap_register_element( rc->heap, &vo->out ); return( vo ); } static void * vo_gather_required( PElement *item, Vo *vo ) { if( vo->nargs_supplied >= MAX_VIPS_ARGS ) { error_top( _( "Too many arguments." ) ); error_sub( _( "No more than %d arguments allowed." ), MAX_VIPS_ARGS ); return( item ); } vo->args[vo->nargs_supplied] = *item; vo->nargs_supplied += 1; return( NULL ); } static int vo_set_property( Vo *vo, const char *name, GParamSpec *pspec, GValue *value ) { /* If we're setting an enum from a string, look up the enum nickname. */ if( G_IS_PARAM_SPEC_ENUM( pspec ) && G_VALUE_TYPE( value ) == VIPS_TYPE_REF_STRING ) { const char *str = vips_value_get_ref_string( value, NULL ); if( vips_object_set_argument_from_string( vo->object, name, str ) ) return( -1 ); } else g_object_set_property( G_OBJECT( vo->object ), name, value ); return( 0 ); } static void * vo_set_required_input( VipsObject *object, GParamSpec *pspec, VipsArgumentClass *argument_class, VipsArgumentInstance *argument_instance, Vo *vo ) { /* Looking for required input args ... these are the ones we can set * from the supplied required list. */ if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) && (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) && (argument_class->flags & VIPS_ARGUMENT_INPUT) && !argument_instance->assigned && vo->nargs_required < vo->nargs_supplied ) { const char *name = g_param_spec_get_name( pspec ); int i = vo->nargs_required; GValue gvalue = { 0 }; if( !heap_ip_to_gvalue( &vo->args[i], &gvalue ) ) return( object ); if( vo_set_property( vo, name, pspec, &gvalue ) ) { g_value_unset( &gvalue ); return( object ); } g_value_unset( &gvalue ); vo->nargs_required += 1; } return( NULL ); } static void * vo_set_optional_arg( const char *name, PElement *value, Vo *vo ) { GParamSpec *pspec; VipsArgumentClass *argument_class; VipsArgumentInstance *argument_instance; /* Looking for construct-time optional input args. */ /* For optional args, we should ignore properties that don't exist. For * example, we might supply ($sharpening => 12) to all interpolators, * though only one interpolator uses this property. */ if( vips_object_get_argument( vo->object, name, &pspec, &argument_class, &argument_instance ) ) return( NULL ); if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) && (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) && (argument_class->flags & VIPS_ARGUMENT_INPUT) && !argument_instance->assigned ) { GValue gvalue = { 0 }; if( !heap_ip_to_gvalue( value, &gvalue ) ) { g_value_unset( &gvalue ); return( value ); } if( vo_set_property( vo, name, pspec, &gvalue ) ) { g_value_unset( &gvalue ); return( value ); } g_value_unset( &gvalue ); } return( NULL ); } /* Set a set of optional args ... of the form [["caption", 12], ["label", 42]] * etc. */ static gboolean vo_set_optional( Vo *vo, PElement *optional ) { if( heap_map_dict( optional, (heap_map_dict_fn) vo_set_optional_arg, vo, NULL ) ) return( FALSE ); return( TRUE ); } /* Make a vo and supply args from nip2. */ static gboolean vo_args( Vo *vo, PElement *required, PElement *optional ) { /* Gather supplied required input args list. */ if( heap_map_list( required, (heap_map_list_fn) vo_gather_required, vo, NULL ) ) return( FALSE ); /* Set required input arguments. */ if( vips_argument_map( VIPS_OBJECT( vo->object ), (VipsArgumentMapFn) vo_set_required_input, vo, NULL ) ) return( FALSE ); if( vo->nargs_supplied != vo->nargs_required ) { error_top( _( "Wrong number of required arguments." ) ); error_sub( _( "Operation \"%s\" has %d required arguments, " "you supplied %d." ), vo->name, vo->nargs_required, vo->nargs_supplied ); return( FALSE ); } /* Set all optional input args. */ if( !vo_set_optional( vo, optional ) ) return( FALSE ); return( TRUE ); } /* Make a VipsObject. */ void vo_object_new( Reduce *rc, const char *name, PElement *required, PElement *optional, PElement *out ) { Vo *vo; Managedgobject *managedgobject; if( !(vo = vo_new( rc, name )) ) reduce_throw( rc ); if( !vo_args( vo, required, optional ) ) { vo_free( vo ); reduce_throw( rc ); } /* Ask the object to construct. */ if( vips_object_build( vo->object ) ) { error_top( _( "VIPS library error." ) ); error_sub( "%s", im_error_buffer() ); im_error_clear(); vo_free( vo ); reduce_throw( rc ); } /* Return the constructed object. */ if( !(managedgobject = managedgobject_new( vo->rc->heap, G_OBJECT( vo->object ) )) ) { vo_free( vo ); reduce_throw( rc ); } PEPUTP( out, ELEMENT_MANAGED, managedgobject ); #ifdef DEBUG { char txt[1000]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_object_to_string( vo->object, &buf ); printf( "vo_object_new: built %s\n", vips_buf_all( &buf ) ); } #endif /*DEBUG*/ vo_free( vo ); } /* Looking for required output args ... append to out. */ static void * vo_get_required_output( VipsObject *object, GParamSpec *pspec, VipsArgumentClass *argument_class, VipsArgumentInstance *argument_instance, Vo *vo, PElement *out ) { if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) && (argument_class->flags & VIPS_ARGUMENT_OUTPUT) && argument_instance->assigned ) { const char *name = g_param_spec_get_name( pspec ); GType type = G_PARAM_SPEC_VALUE_TYPE( pspec ); PElement lhs; GValue value = { 0 }; if( !heap_list_add( vo->rc->heap, out, &lhs ) ) return( object ); g_value_init( &value, type ); g_object_get_property( G_OBJECT( object ), name, &value ); if( !heap_gvalue_to_ip( &value, &lhs ) ) { g_value_unset( &value ); return( object ); } g_value_unset( &value ); (void) heap_list_next( out ); } return( NULL ); } /* Looking for construct-time optional output args. Append them to out. */ static void * vo_get_optional_arg( const char *name, PElement *value, Vo *vo, PElement *out ) { GParamSpec *pspec; VipsArgumentClass *argument_class; VipsArgumentInstance *argument_instance; if( vips_object_get_argument( vo->object, name, &pspec, &argument_class, &argument_instance ) ) return( NULL ); if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) && (argument_class->flags & VIPS_ARGUMENT_OUTPUT) && argument_instance->assigned ) { GType type = G_PARAM_SPEC_VALUE_TYPE( pspec ); GValue gvalue = { 0 }; PElement lhs; if( !heap_list_add( vo->rc->heap, out, &lhs ) ) return( value ); g_value_init( &gvalue, type ); g_object_get_property( G_OBJECT( vo->object ), name, &gvalue ); if( !heap_gvalue_to_ip( &gvalue, &lhs ) ) { g_value_unset( &gvalue ); return( value ); } g_value_unset( &gvalue ); (void) heap_list_next( out ); } return( NULL ); } /* Get a set of optional args ... of the form [["caption", []], ["label", []]] * etc. */ static gboolean vo_get_optional( Vo *vo, PElement *optional, PElement *out ) { if( heap_map_dict( optional, (heap_map_dict_fn) vo_get_optional_arg, vo, out ) ) return( FALSE ); return( TRUE ); } /* Run a VipsOperation. Like vo_object_new(), but we return the output args * rather than the operation. */ void vo_call( Reduce *rc, const char *name, PElement *required, PElement *optional, PElement *out ) { Vo *vo; PElement pe; if( !(vo = vo_new( rc, name )) ) reduce_throw( rc ); if( !vo_args( vo, required, optional ) ) { vo_free( vo ); reduce_throw( rc ); } /* Ask the object to construct. This can update vo->operation with an * old one from the cache. */ if( vips_cache_operation_buildp( (VipsOperation **) &vo->object ) ) { error_top( _( "VIPS library error." ) ); error_sub( "%s", im_error_buffer() ); im_error_clear(); vips_object_unref_outputs( vo->object ); vo_free( vo ); reduce_throw( rc ); } /* We can't build the output object directly on out, since it might be * one of our inputs. We use the safe Element in vo for the build, * then copy at the end. */ /* Empty output list. */ PEPOINTE( &pe, &vo->out ); heap_list_init( &pe ); /* Append required outputs. */ if( vips_argument_map( VIPS_OBJECT( vo->object ), (VipsArgumentMapFn) vo_get_required_output, vo, &pe ) ) { vips_object_unref_outputs( vo->object ); vo_free( vo ); reduce_throw( rc ); } /* Append optional outputs. */ if( !vo_get_optional( vo, optional, &pe ) ) { vips_object_unref_outputs( vo->object ); vo_free( vo ); reduce_throw( rc ); } /* Now write the output object to out. */ PEPUTE( out, &vo->out ); vips_object_unref_outputs( vo->object ); vo_free( vo ); } ================================================ FILE: src/vipsobject.h ================================================ /* Links to VipsObject. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ void vo_object_new( Reduce *rc, const char *name, PElement *required, PElement *optional, PElement *out ); void vo_call( Reduce *rc, const char *name, PElement *required, PElement *optional, PElement *out ); ================================================ FILE: src/vobject.c ================================================ /* abstract base class for a vobject object ... watch an iobject and call * _refresh in idle if it changes. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Time each refresh #define DEBUG_TIME */ #include "ip.h" static GtkVBoxClass *parent_class = NULL; static Queue *vobject_dirty = NULL; static gint vobject_refresh_timeout = 0; /* Remove from refresh queue. */ static void vobject_refresh_dequeue( vObject *vobject ) { if( vobject->dirty ) { #ifdef DEBUG printf( "vobject_refresh_dequeue: \"%s\"\n", G_OBJECT_TYPE_NAME( vobject ) ); #endif /*DEBUG*/ vobject->dirty = FALSE; queue_remove( vobject_dirty, vobject ); } } #ifdef DEBUG_TIME /* Refresh all vobjects at once and time them. */ static gboolean vobject_refresh_timeout_cb( gpointer user_data ) { static GTimer *refresh_timer = NULL; double last_elapsed; double worst_time = 0.0; int worst_index = -1; void *data; int n; vobject_refresh_timeout = 0; if( !refresh_timer ) refresh_timer = g_timer_new(); g_timer_reset( refresh_timer ); printf( "vobject_idle_refresh: starting ...\n" ); for( n = 0; (data = queue_head( vobject_dirty )); n++ ) { vObject *vobject = VOBJECT( data ); double elapsed; vobject->dirty = FALSE; vobject_refresh( vobject ); elapsed = g_timer_elapsed( refresh_timer, NULL ); if( elapsed - last_elapsed > worst_time ) { worst_time = elapsed - last_elapsed; worst_index = n; } last_elapsed = elapsed; } printf( "vobject_idle_refresh: done after %gs (%d refreshes)\n", g_timer_elapsed( refresh_timer, NULL ), n ); printf( "vobject_idle_refresh: worst %gs (refresh %d)\n", worst_time, worst_index ); return( FALSE ); } #else /*DEBUG_TIME*/ /* Refresh stuff off the dirty list. */ static gboolean vobject_refresh_timeout_cb( gpointer user_data ) { void *data; #ifdef DEBUG printf( "vobject_refresh_timeout_cb:\n" ); #endif /*DEBUG*/ vobject_refresh_timeout = 0; while( (data = queue_head( vobject_dirty ) ) ) { vObject *vobject = VOBJECT( data ); #ifdef DEBUG printf( "vobject_refresh_timeout_cb: starting \"%s\" (%p)\n", G_OBJECT_TYPE_NAME( vobject ), vobject ); #endif /*DEBUG*/ /* We must clear dirty before we _refresh() so that if the * _refresh() indirectly triggers another update, we will * _refresh() again. */ vobject->dirty = FALSE; vobject_refresh( vobject ); } return( FALSE ); } #endif /*DEBUG_TIME*/ /* Mark something for refresh. Seldom call this directly ... just change the * iobject and all vobjects will have a refresh queued. */ void * vobject_refresh_queue( vObject *vobject ) { if( !vobject->dirty ) { #ifdef DEBUG printf( "vobject_refresh_queue: %s (%p)", G_OBJECT_TYPE_NAME( vobject ), vobject ); if( vobject->iobject ) printf( ", iobject %s \"%s\"", G_OBJECT_TYPE_NAME( vobject->iobject ), NN( vobject->iobject->name ) ); printf( "\n" ); #endif /*DEBUG*/ vobject->dirty = TRUE; queue_add( vobject_dirty, vobject ); IM_FREEF( g_source_remove, vobject_refresh_timeout ); vobject_refresh_timeout = g_timeout_add( 20, (GSourceFunc) vobject_refresh_timeout_cb, NULL ); } return( NULL ); } /* Called for iobject changed signal ... queue a refresh. */ static void vobject_iobject_changed( iObject *iobject, vObject *vobject ) { #ifdef DEBUG printf( "vobject_iobject_changed: %s %s \"%s\"\n", G_OBJECT_TYPE_NAME( vobject ), G_OBJECT_TYPE_NAME( iobject ), NN( iobject->name ) ); #endif /*DEBUG*/ vobject_refresh_queue( vobject ); } /* Called for iobject destroy signal ... kill the vobject too. */ static void vobject_iobject_destroy( iObject *iobject, vObject *vobject ) { #ifdef DEBUG printf( "vobject_iobject_destroy: iobject %s \"%s\"\n", G_OBJECT_TYPE_NAME( iobject ), NN( iobject->name ) ); #endif /*DEBUG*/ gtk_widget_destroy( GTK_WIDGET( vobject ) ); } /* Link to iobject. */ void vobject_link( vObject *vobject, iObject *iobject ) { vObjectClass *vobject_class = VOBJECT_GET_CLASS( vobject ); g_assert( !vobject->iobject ); if( vobject_class->link ) vobject_class->link( vobject, iobject ); /* Queue a refresh ... we always need at least one. */ vobject_refresh_queue( vobject ); } static void vobject_destroy( GtkObject *object ) { vObject *vobject; g_return_if_fail( object != NULL ); g_return_if_fail( IS_VOBJECT( object ) ); vobject = VOBJECT( object ); #ifdef DEBUG printf( "vobject_destroy: \"%s\"\n", G_OBJECT_TYPE_NAME( object ) ); #endif /*DEBUG*/ if( vobject->iobject ) { FREESID( vobject->changed_sid, vobject->iobject ); FREESID( vobject->destroy_sid, vobject->iobject ); vobject->iobject = NULL; } vobject_refresh_dequeue( vobject ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void vobject_finalize( GObject *gobject ) { #ifdef DEBUG printf( "vobject_finalize: \"%s\"\n", G_OBJECT_TYPE_NAME( gobject ) ); #endif /*DEBUG*/ G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void vobject_real_refresh( vObject *vobject ) { #ifdef DEBUG printf( "vobject_real_refresh: %p %s\n", vobject, G_OBJECT_TYPE_NAME( vobject ) ); #endif /*DEBUG*/ } static void vobject_real_link( vObject *vobject, iObject *iobject ) { vobject->iobject = iobject; vobject->changed_sid = g_signal_connect( iobject, "changed", G_CALLBACK( vobject_iobject_changed ), vobject ); vobject->destroy_sid = g_signal_connect( iobject, "destroy", G_CALLBACK( vobject_iobject_destroy ), vobject ); } static void vobject_class_init( vObjectClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); GtkObjectClass *object_class = (GtkObjectClass*) class; parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = vobject_finalize; object_class->destroy = vobject_destroy; /* Create signals. */ /* Init default methods. */ class->refresh = vobject_real_refresh; class->link = vobject_real_link; /* Static init. */ vobject_dirty = queue_new(); } static void vobject_init( vObject *vobject ) { /* Init our instance fields. */ vobject->iobject = NULL; vobject->changed_sid = 0; vobject->destroy_sid = 0; vobject->dirty = FALSE; /* All new vobjects will need refreshing. */ vobject_refresh_queue( vobject ); } GtkType vobject_get_type( void ) { static GtkType vobject_type = 0; if( !vobject_type ) { static const GtkTypeInfo vobject_info = { "vObject", sizeof( vObject ), sizeof( vObjectClass ), (GtkClassInitFunc) vobject_class_init, (GtkObjectInitFunc) vobject_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; vobject_type = gtk_type_unique( GTK_TYPE_VBOX, &vobject_info ); } return( vobject_type ); } /* Trigger the refresh method for a vobject immediately ... we usually queue * and wait for idle, but this can be better for interactive stuff. */ void * vobject_refresh( vObject *vobject ) { vObjectClass *vobject_class = VOBJECT_GET_CLASS( vobject ); if( vobject_class->refresh ) vobject_class->refresh( vobject ); return( NULL ); } ================================================ FILE: src/vobject.h ================================================ /* abstract base class for a view ... watch an iobject and call _refresh in * idle if it changes. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_VOBJECT (vobject_get_type()) #define VOBJECT( obj ) (GTK_CHECK_CAST( (obj), TYPE_VOBJECT, vObject )) #define VOBJECT_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), TYPE_VOBJECT, vObjectClass )) #define IS_VOBJECT( obj ) (GTK_CHECK_TYPE( (obj), TYPE_VOBJECT )) #define IS_VOBJECT_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_VOBJECT )) #define VOBJECT_GET_CLASS( obj ) \ (GTK_CHECK_GET_CLASS( (obj), TYPE_VOBJECT, vObjectClass )) struct _vObject { GtkVBox vbox; /* My instance vars. */ iObject *iobject; /* iObject we are watching */ guint changed_sid; /* Signals we use to watch model */ guint destroy_sid; gboolean dirty; /* In need of refreshment */ }; typedef struct _vObjectClass { GtkVBoxClass parent_class; /* State change refresh refresh widgets (don't look at heap value, look at model) link this vobject has been linked to an iobject we also have View::link() -- vObject::link is a lower-level link which is handy for views which are not Views, eg. toolkitbrowser */ void (*refresh)( vObject * ); void (*link)( vObject *, iObject * ); } vObjectClass; void *vobject_refresh_queue( vObject *vobject ); GtkType vobject_get_type( void ); void vobject_base_init( void ); void vobject_link( vObject *vobject, iObject *iobject ); void *vobject_refresh( vObject *vobject ); ================================================ FILE: src/watch.c ================================================ /* Watch stuff in the prefs workspace. */ /* 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 watch) any later version. This program is distributed in the hope that 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static iContainerClass *watchgroup_parent_class = NULL; /* Our signals. */ enum { SIG_WATCH_CHANGED, /* "changed" on one of our watches */ SIG_LAST }; static guint watchgroup_signals[SIG_LAST] = { 0 }; static void watchgroup_changed( Watchgroup *watchgroup, Watch *watch ) { g_signal_emit( G_OBJECT( watchgroup ), watchgroup_signals[SIG_WATCH_CHANGED], 0, watch ); } static void watchgroup_class_init( WatchgroupClass *class ) { watchgroup_parent_class = g_type_class_peek_parent( class ); watchgroup_signals[SIG_WATCH_CHANGED] = g_signal_new( "watch_changed", G_OBJECT_CLASS_TYPE( class ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET( WatchgroupClass, watch_changed ), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, TYPE_WATCH ); } static void watchgroup_init( Watchgroup *watchgroup ) { #ifdef DEBUG printf( "watchgroup_init\n" ); #endif /*DEBUG*/ watchgroup->auto_save_timeout = 0; } GType watchgroup_get_type( void ) { static GType watchgroup_type = 0; if( !watchgroup_type ) { static const GTypeInfo info = { sizeof( WatchgroupClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) watchgroup_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Watchgroup ), 32, /* n_preallocs */ (GInstanceInitFunc) watchgroup_init, }; watchgroup_type = g_type_register_static( TYPE_ICONTAINER, "Watchgroup", &info, 0 ); } return( watchgroup_type ); } Watchgroup * watchgroup_new( Workspaceroot *workspaceroot, const char *name ) { Watchgroup *watchgroup = WATCHGROUP( g_object_new( TYPE_WATCHGROUP, NULL ) ); /* Assume it's a static string. */ watchgroup->name = name; watchgroup->workspaceroot = workspaceroot; icontainer_set_hash( ICONTAINER( watchgroup ) ); return( watchgroup ); } /* Get the ws we are storing prefs in, and check it looks OK. */ static Workspace * watchgroup_get_workspace( Watchgroup *watchgroup ) { Compile *compile; Symbol *sym; if( !watchgroup->workspaceroot->sym ) return( NULL ); compile = watchgroup->workspaceroot->sym->expr->compile; if( !(sym = compile_lookup( compile, watchgroup->name )) || !sym->expr->compile || sym->type != SYM_WORKSPACE || !sym->ws ) return( NULL ); return( sym->ws ); } static void watchgroup_save( Watchgroup *watchgroup ) { Workspace *ws; if( (ws = watchgroup_get_workspace( watchgroup )) ) { Workspacegroup *wsg = workspace_get_workspacegroup( ws ); Filemodel *filemodel = FILEMODEL( wsg ); if( filemodel->modified ) { symbol_recalculate_all(); /* Ignore error returns ... hmm! Tricky: we can come * here during shutdown. */ (void) filemodel_top_save( filemodel, filemodel->filename ); filemodel_set_modified( filemodel, FALSE ); } } } static gboolean watchgroup_dirty_timeout_cb( Watchgroup *watchgroup ) { watchgroup->auto_save_timeout = 0; watchgroup_save( watchgroup ); return( FALSE ); } void watchgroup_dirty( Watchgroup *watchgroup ) { Workspace *ws; /* Find the preferences workspace. */ if( (ws = watchgroup_get_workspace( watchgroup )) ) { Workspacegroup *wsg = workspace_get_workspacegroup( ws ); /* Mark ws dirty, start save timer. */ filemodel_set_modified( FILEMODEL( wsg ), TRUE ); IM_FREEF( g_source_remove, watchgroup->auto_save_timeout ); watchgroup->auto_save_timeout = g_timeout_add( 1000, (GSourceFunc) watchgroup_dirty_timeout_cb, watchgroup ); } } void watchgroup_flush( Watchgroup *watchgroup ) { /* Do we have a pending save? */ if( watchgroup->auto_save_timeout ) { watchgroup_save( watchgroup ); IM_FREEF( g_source_remove, watchgroup->auto_save_timeout ); } } static iContainerClass *watch_parent_class = NULL; static GSList *watch_all = NULL; static void watch_finalize( GObject *gobject ) { Watch *watch; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WATCH( gobject ) ); watch = WATCH( gobject ); #ifdef DEBUG printf( "watch_finalize: %s\n", NN( IOBJECT( watch )->name ) ); #endif /*DEBUG*/ watch_all = g_slist_remove( watch_all, watch ); G_OBJECT_CLASS( watch_parent_class )->finalize( gobject ); } static void watch_dispose( GObject *gobject ) { Watch *watch; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WATCH( gobject ) ); watch = WATCH( gobject ); #ifdef DEBUG printf( "watch_dispose: %s\n", NN( IOBJECT( watch )->name ) ); #endif /*DEBUG*/ /* My instance destroy stuff. */ FREESID( watch->destroy_sid, watch->row ); FREESID( watch->changed_sid, watch->row ); watch->row = NULL; G_OBJECT_CLASS( watch_parent_class )->dispose( gobject ); } static void watch_changed( iObject *iobject ) { Watch *watch = WATCH( iobject ); Watchgroup *watchgroup = WATCHGROUP( ICONTAINER( watch )->parent ); /* Emit on our group too. Can get here before our parent is linked on, * careful. */ if( watchgroup ) watchgroup_changed( WATCHGROUP( ICONTAINER( watch )->parent ), watch ); IOBJECT_CLASS( watch_parent_class )->changed( iobject ); } static void watch_class_init( WatchClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; WatchClass *watch_class = (WatchClass *) class; watch_parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = watch_finalize; gobject_class->dispose = watch_dispose; iobject_class->changed = watch_changed; watch_class->update = NULL; watch_class->get_value = NULL; } static void watch_init( Watch *watch ) { watch->row = NULL; watch->ok = FALSE; watch->destroy_sid = 0; watch->changed_sid = 0; watch_all = g_slist_prepend( watch_all, watch ); } GType watch_get_type( void ) { static GType watch_type = 0; if( !watch_type ) { static const GTypeInfo info = { sizeof( WatchClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) watch_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Watch ), 32, /* n_preallocs */ (GInstanceInitFunc) watch_init, }; watch_type = g_type_register_static( TYPE_ICONTAINER, "Watch", &info, 0 ); } return( watch_type ); } static void watch_link( Watch *watch, Watchgroup *watchgroup, const char *name ) { iobject_set( IOBJECT( watch ), name, NULL ); icontainer_child_add( ICONTAINER( watchgroup ), ICONTAINER( watch ), -1 ); } static void watch_destroy_cb( Row *row, Watch *watch ) { #ifdef DEBUG printf( "watch_destroy_cb\n" ); #endif /*DEBUG*/ watch->row = NULL; watch->ok = FALSE; watch->destroy_sid = 0; watch->changed_sid = 0; } /* The row we are watching has changed. */ static void watch_changed_cb( Row *row, Watch *watch ) { #ifdef DEBUG printf( "watch_changed_cb: %s\n", NN( IOBJECT( watch )->name ) ); #endif /*DEBUG*/ if( row->expr ) watch->ok = WATCH_GET_CLASS( watch )->update( watch ); iobject_changed( IOBJECT( watch ) ); } /* Make sure we're linked to the thing we watch. */ static void watch_attach( Watch *watch ) { Watchgroup *watchgroup = WATCHGROUP( ICONTAINER( watch )->parent ); const char *name = IOBJECT( watch )->name; Workspace *ws; Symbol *sym; if( watch->row ) return; if( (ws = watchgroup_get_workspace( watchgroup )) && ws->sym->expr && ws->sym->expr->compile && (sym = compile_lookup( ws->sym->expr->compile, name )) && sym->expr->row ) { watch->row = sym->expr->row; watch->destroy_sid = g_signal_connect( G_OBJECT( watch->row ), "destroy", G_CALLBACK( watch_destroy_cb ), watch ); watch->changed_sid = g_signal_connect( G_OBJECT( watch->row ), "changed", G_CALLBACK( watch_changed_cb ), watch ); } } Watch * watch_find( Watchgroup *watchgroup, const char *name ) { return( (Watch *) (icontainer_child_lookup( ICONTAINER( watchgroup ), name )) ); } static gboolean watch_get( Watch *watch, void **out ) { #ifdef DEBUG printf( "watch_get: %s\n", NN( IOBJECT( watch )->name ) ); #endif /*DEBUG*/ watch_attach( watch ); if( !watch->row ) return( FALSE ); if( !watch->ok ) watch->ok = WATCH_GET_CLASS( watch )->update( watch ); if( !watch->ok ) return( FALSE ); *out = WATCH_GET_CLASS( watch )->get_value( watch ); return( TRUE ); } static void * watch_relink( Watch *watch ) { if( !watch->row ) { watch_attach( watch ); if( watch->row ) iobject_changed( IOBJECT( watch ) ); } return( NULL ); } void watch_relink_all( void ) { slist_map( watch_all, (SListMapFn) watch_relink, NULL ); } void watch_vset( Watch *watch, const char *fmt, va_list args ) { Watchgroup *watchgroup = WATCHGROUP( ICONTAINER( watch )->parent ); Workspace *ws; /* In case we try to set after prefs has gone. */ if( !(ws = watchgroup_get_workspace( watchgroup )) || ws->in_dispose ) return; if( watch->row && watch->row->child_rhs && watch->row->child_rhs->itext ) { iText *itext = ITEXT( watch->row->child_rhs->itext ); char buf[256]; (void) im_vsnprintf( buf, 256, fmt, args ); if( itext_set_formula( itext, buf ) ) { #ifdef DEBUG printf( "watch_vset: %s = %s\n", IOBJECT( watch )->name, buf ); #endif /*DEBUG*/ itext_set_edited( itext, TRUE ); if( watch->row->sym ) expr_dirty( watch->row->sym->expr, link_serial_new() ); watchgroup_dirty( WATCHGROUP( ICONTAINER( watch )->parent ) ); } } } void watch_set( Watch *watch, const char *fmt, ... ) { va_list args; va_start( args, fmt ); watch_vset( watch, fmt, args ); va_end( args ); } static WatchClass *watch_double_parent_class = NULL; static gboolean watch_double_update( Watch *watch ) { WatchDouble *watch_double = WATCH_DOUBLE( watch ); PElement *root = &watch->row->expr->root; #ifdef DEBUG printf( "watch_double_update\n" ); #endif /*DEBUG*/ if( PEISNOVAL( root ) ) return( FALSE ); if( !PEISREAL( root ) ) { heap_error_typecheck( root, IOBJECT( watch )->name, "real" ); return( FALSE ); } watch_double->value = PEGETREAL( root ); return( TRUE ); } static void * watch_double_get_value( Watch *watch ) { WatchDouble *watch_double = WATCH_DOUBLE( watch ); return( (void *) &watch_double->value ); } static void watch_double_class_init( WatchDoubleClass *class ) { WatchClass *watch_class = (WatchClass *) class; watch_double_parent_class = g_type_class_peek_parent( class ); watch_class->update = watch_double_update; watch_class->get_value = watch_double_get_value; } static void watch_double_init( WatchDouble *watch_double ) { #ifdef DEBUG printf( "watch_double_init\n" ); #endif /*DEBUG*/ watch_double->value = -1.0; } GType watch_double_get_type( void ) { static GType watch_double_type = 0; if( !watch_double_type ) { static const GTypeInfo info = { sizeof( WatchDoubleClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) watch_double_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( WatchDouble ), 32, /* n_preallocs */ (GInstanceInitFunc) watch_double_init, }; watch_double_type = g_type_register_static( TYPE_WATCH, "WatchDouble", &info, 0 ); } return( watch_double_type ); } static Watch * watch_double_new( Watchgroup *watchgroup, const char *name ) { WatchDouble *watch_double = WATCH_DOUBLE( g_object_new( TYPE_WATCH_DOUBLE, NULL ) ); watch_link( WATCH( watch_double ), watchgroup, name ); return( WATCH( watch_double ) ); } double watch_double_get( Watchgroup *watchgroup, const char *name, double fallback ) { Watch *watch; void *value; if( !watchgroup ) return( fallback ); if( !(watch = watch_find( watchgroup, name )) ) watch = watch_double_new( watchgroup, name ); g_assert( IS_WATCH_DOUBLE( watch ) ); if( !watch_get( watch, &value ) ) return( fallback ); return( *((double *) value) ); } static WatchClass *watch_int_parent_class = NULL; static gboolean watch_int_update( Watch *watch ) { WatchInt *watch_int = WATCH_INT( watch ); Expr *expr = watch->row->expr; PElement *root; #ifdef DEBUG printf( "watch_int_update: %s\n", NN( IOBJECT( watch )->name ) ); #endif /*DEBUG*/ /* Can get called during shutdown :-( main_watchgroup_changed_cb() can * call us before destroying the row, but after killing the expr. */ if( !expr ) return( FALSE ); root = &expr->root; if( PEISNOVAL( root ) ) return( FALSE ); if( !PEISREAL( root ) ) { heap_error_typecheck( root, IOBJECT( watch )->name, "real" ); return( FALSE ); } watch_int->value = IM_RINT( PEGETREAL( root ) ); return( TRUE ); } static void * watch_int_get_value( Watch *watch ) { WatchInt *watch_int = WATCH_INT( watch ); return( (void *) &watch_int->value ); } static void watch_int_class_init( WatchIntClass *class ) { WatchClass *watch_class = (WatchClass *) class; watch_int_parent_class = g_type_class_peek_parent( class ); watch_class->update = watch_int_update; watch_class->get_value = watch_int_get_value; } static void watch_int_init( WatchInt *watch_int ) { #ifdef DEBUG printf( "watch_int_init\n" ); #endif /*DEBUG*/ watch_int->value = -1; } GType watch_int_get_type( void ) { static GType watch_int_type = 0; if( !watch_int_type ) { static const GTypeInfo info = { sizeof( WatchIntClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) watch_int_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( WatchInt ), 32, /* n_preallocs */ (GInstanceInitFunc) watch_int_init, }; watch_int_type = g_type_register_static( TYPE_WATCH, "WatchInt", &info, 0 ); } return( watch_int_type ); } static Watch * watch_int_new( Watchgroup *watchgroup, const char *name ) { WatchInt *watch_int = WATCH_INT( g_object_new( TYPE_WATCH_INT, NULL ) ); watch_link( WATCH( watch_int ), watchgroup, name ); return( WATCH( watch_int ) ); } int watch_int_get( Watchgroup *watchgroup, const char *name, int fallback ) { Watch *watch; void *value; if( !watchgroup || !name ) return( fallback ); if( !(watch = watch_find( watchgroup, name )) ) watch = watch_int_new( watchgroup, name ); g_assert( IS_WATCH_INT( watch ) ); if( !watch_get( watch, &value ) ) return( fallback ); return( *((int *) value) ); } static WatchClass *watch_path_parent_class = NULL; static void watch_path_finalize( GObject *gobject ) { WatchPath *watch_path; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WATCH_PATH( gobject ) ); #ifdef DEBUG printf( "watch_path_finalize\n" ); #endif /*DEBUG*/ watch_path = WATCH_PATH( gobject ); /* My instance destroy stuff. */ IM_FREEF( slist_free_all, watch_path->value ); G_OBJECT_CLASS( watch_path_parent_class )->finalize( gobject ); } static gboolean watch_path_update( Watch *watch ) { WatchPath *watch_path = WATCH_PATH( watch ); PElement *root = &watch->row->expr->root; GSList *value; #ifdef DEBUG printf( "watch_path_update\n" ); #endif /*DEBUG*/ if( !heap_get_lstring( root, &value ) ) return( FALSE ); IM_FREEF( slist_free_all, watch_path->value ); watch_path->value = value; return( TRUE ); } static void * watch_path_get_value( Watch *watch ) { WatchPath *watch_path = WATCH_PATH( watch ); return( (void *) &watch_path->value ); } static void watch_path_class_init( WatchPathClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; WatchClass *watch_class = (WatchClass *) class; watch_path_parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = watch_path_finalize; watch_class->update = watch_path_update; watch_class->get_value = watch_path_get_value; } static void watch_path_init( WatchPath *watch_path ) { #ifdef DEBUG printf( "watch_path_init\n" ); #endif /*DEBUG*/ watch_path->value = NULL; } GType watch_path_get_type( void ) { static GType watch_path_type = 0; if( !watch_path_type ) { static const GTypeInfo info = { sizeof( WatchPathClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) watch_path_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( WatchPath ), 32, /* n_preallocs */ (GInstanceInitFunc) watch_path_init, }; watch_path_type = g_type_register_static( TYPE_WATCH, "WatchPath", &info, 0 ); } return( watch_path_type ); } static Watch * watch_path_new( Watchgroup *watchgroup, const char *name ) { WatchPath *watch_path = WATCH_PATH( g_object_new( TYPE_WATCH_PATH, NULL ) ); watch_link( WATCH( watch_path ), watchgroup, name ); return( WATCH( watch_path ) ); } GSList * watch_path_get( Watchgroup *watchgroup, const char *name, GSList *fallback ) { Watch *watch; void *value; if( !watchgroup ) return( fallback ); if( !(watch = watch_find( watchgroup, name )) ) watch = watch_path_new( watchgroup, name ); g_assert( IS_WATCH_PATH( watch ) ); if( !watch_get( watch, &value ) ) return( fallback ); return( *((GSList **) value) ); } static WatchClass *watch_bool_parent_class = NULL; static gboolean watch_bool_update( Watch *watch ) { WatchBool *watch_bool = WATCH_BOOL( watch ); PElement *root = &watch->row->expr->root; #ifdef DEBUG printf( "watch_bool_update: %s\n", NN( IOBJECT( watch )->name ) ); #endif /*DEBUG*/ if( PEISNOVAL( root ) ) return( FALSE ); if( !PEISBOOL( root ) ) { heap_error_typecheck( root, IOBJECT( watch )->name, "bool" ); return( FALSE ); } watch_bool->value = PEGETBOOL( root ); return( TRUE ); } static void * watch_bool_get_value( Watch *watch ) { WatchBool *watch_bool = WATCH_BOOL( watch ); return( (void *) &watch_bool->value ); } static void watch_bool_class_init( WatchBoolClass *class ) { WatchClass *watch_class = (WatchClass *) class; watch_bool_parent_class = g_type_class_peek_parent( class ); watch_class->update = watch_bool_update; watch_class->get_value = watch_bool_get_value; } static void watch_bool_init( WatchBool *watch_bool ) { #ifdef DEBUG printf( "watch_bool_init\n" ); #endif /*DEBUG*/ watch_bool->value = FALSE; } GType watch_bool_get_type( void ) { static GType watch_bool_type = 0; if( !watch_bool_type ) { static const GTypeInfo info = { sizeof( WatchBoolClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) watch_bool_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( WatchBool ), 32, /* n_preallocs */ (GInstanceInitFunc) watch_bool_init, }; watch_bool_type = g_type_register_static( TYPE_WATCH, "WatchBool", &info, 0 ); } return( watch_bool_type ); } static Watch * watch_bool_new( Watchgroup *watchgroup, const char *name ) { WatchBool *watch_bool = WATCH_BOOL( g_object_new( TYPE_WATCH_BOOL, NULL ) ); watch_link( WATCH( watch_bool ), watchgroup, name ); return( WATCH( watch_bool ) ); } gboolean watch_bool_get( Watchgroup *watchgroup, const char *name, gboolean fallback ) { Watch *watch; void *value; if( !watchgroup ) return( fallback ); if( !(watch = watch_find( watchgroup, name )) ) watch = watch_bool_new( watchgroup, name ); g_assert( IS_WATCH_BOOL( watch ) ); if( !watch_get( watch, &value ) ) return( fallback ); return( *((gboolean *) value) ); } static WatchClass *watch_string_parent_class = NULL; static void watch_string_finalize( GObject *gobject ) { WatchString *watch_string; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WATCH_STRING( gobject ) ); #ifdef DEBUG printf( "watch_string_finalize\n" ); #endif /*DEBUG*/ watch_string = WATCH_STRING( gobject ); /* My instance destroy stuff. */ IM_FREE( watch_string->value ); G_OBJECT_CLASS( watch_string_parent_class )->finalize( gobject ); } static gboolean watch_string_update( Watch *watch ) { WatchString *watch_string = WATCH_STRING( watch ); PElement *root = &watch->row->expr->root; char value[1024]; #ifdef DEBUG printf( "watch_string_update\n" ); #endif /*DEBUG*/ if( !heap_get_string( root, value, 1024 ) ) return( FALSE ); IM_SETSTR( watch_string->value, value ); return( TRUE ); } static void * watch_string_get_value( Watch *watch ) { WatchString *watch_string = WATCH_STRING( watch ); return( (void *) &watch_string->value ); } static void watch_string_class_init( WatchStringClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; WatchClass *watch_class = (WatchClass *) class; watch_string_parent_class = g_type_class_peek_parent( class ); gobject_class->finalize = watch_string_finalize; watch_class->update = watch_string_update; watch_class->get_value = watch_string_get_value; } static void watch_string_init( WatchString *watch_string ) { #ifdef DEBUG printf( "watch_string_init\n" ); #endif /*DEBUG*/ watch_string->value = NULL; } GType watch_string_get_type( void ) { static GType watch_string_type = 0; if( !watch_string_type ) { static const GTypeInfo info = { sizeof( WatchStringClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) watch_string_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( WatchString ), 32, /* n_preallocs */ (GInstanceInitFunc) watch_string_init, }; watch_string_type = g_type_register_static( TYPE_WATCH, "WatchString", &info, 0 ); } return( watch_string_type ); } static Watch * watch_string_new( Watchgroup *watchgroup, const char *name ) { WatchString *watch_string = WATCH_STRING( g_object_new( TYPE_WATCH_STRING, NULL ) ); watch_link( WATCH( watch_string ), watchgroup, name ); return( WATCH( watch_string ) ); } const char * watch_string_get( Watchgroup *watchgroup, const char *name, const char *fallback ) { Watch *watch; void *value; if( !watchgroup ) return( fallback ); if( !(watch = watch_find( watchgroup, name )) ) watch = watch_string_new( watchgroup, name ); g_assert( IS_WATCH_STRING( watch ) ); if( !watch_get( watch, &value ) ) return( fallback ); return( *((const char **) value) ); } ================================================ FILE: src/watch.h ================================================ /* Watch stuff in the prefs workspace. */ /* 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 watch) any later version. This program is distributed in the hope that 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* Group watches with this. */ #define TYPE_WATCHGROUP (watchgroup_get_type()) #define WATCHGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WATCHGROUP, Watchgroup )) #define WATCHGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WATCHGROUP, WatchgroupClass)) #define IS_WATCHGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WATCHGROUP )) #define IS_WATCHGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WATCHGROUP )) #define WATCHGROUP_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WATCHGROUP, WatchgroupClass )) typedef struct _Watchgroup { iContainer parent_object; /* Workspaces we work within. Assume we are destroyed before this. */ Workspaceroot *workspaceroot; /* Name of workspace our watchers check for their syms. */ const char *name; /* Autosave timeout ... save our workspace automatically when this * ticks away. */ guint auto_save_timeout; } Watchgroup; typedef struct _WatchgroupClass { iContainerClass parent_class; /* One of the watches in this group has changed. * People interested in several watches can connect to * this, rather than having to try listening for many "changed" signals * on the watches. */ void (*watch_changed)( Watchgroup *, Watch * ); } WatchgroupClass; GType watchgroup_get_type( void ); Watchgroup *watchgroup_new( Workspaceroot *workspaceroot, const char *name ); void watchgroup_flush( Watchgroup *watchgroup ); /* Abstract base class for something that watches a row. */ #define TYPE_WATCH (watch_get_type()) #define WATCH( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WATCH, Watch )) #define WATCH_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WATCH, WatchClass)) #define IS_WATCH( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WATCH )) #define IS_WATCH_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WATCH )) #define WATCH_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WATCH, WatchClass )) typedef void (*WatchCallbackFn)( void * ); struct _Watch { iContainer parent_class; Row *row; /* Row we watch */ gboolean ok; /* Value read OK on last change */ guint destroy_sid; /* Listen for events */ guint changed_sid; }; typedef struct _WatchClass { iContainerClass parent_class; /* Update value from row. */ gboolean (*update)( Watch * ); /* Get a pointer to value. */ void *(*get_value)( Watch * ); } WatchClass; Watch *watch_find( Watchgroup *watchgroup, const char *name ); GtkType watch_get_type( void ); void watch_relink_all( void ); void watch_vset( Watch *watch, const char *fmt, va_list args ); void watch_set( Watch *watch, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); /* A watch that watches something with an int value. */ typedef struct _WatchInt WatchInt; #define TYPE_WATCH_INT (watch_int_get_type()) #define WATCH_INT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WATCH_INT, WatchInt )) #define WATCH_INT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WATCH_INT, WatchIntClass)) #define IS_WATCH_INT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WATCH_INT )) #define IS_WATCH_INT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WATCH_INT )) #define WATCH_INT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WATCH_INT, WatchIntClass )) struct _WatchInt { Watch parent_class; int value; }; typedef struct _WatchIntClass { WatchClass parent_class; } WatchIntClass; GtkType watch_int_get_type( void ); int watch_int_get( Watchgroup *, const char *name, int fallback ); /* A watch that watches something with a double value. */ typedef struct _WatchDouble WatchDouble; #define TYPE_WATCH_DOUBLE (watch_double_get_type()) #define WATCH_DOUBLE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WATCH_DOUBLE, WatchDouble )) #define WATCH_DOUBLE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WATCH_DOUBLE, WatchDoubleClass)) #define IS_WATCH_DOUBLE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WATCH_DOUBLE )) #define IS_WATCH_DOUBLE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WATCH_DOUBLE )) #define WATCH_DOUBLE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WATCH_DOUBLE, \ WatchDoubleClass )) struct _WatchDouble { Watch parent_class; double value; }; typedef struct _WatchDoubleClass { WatchClass parent_class; } WatchDoubleClass; GtkType watch_double_get_type( void ); double watch_double_get( Watchgroup *, const char *name, double fallback ); /* A watch that watches a path. */ typedef struct _WatchPath WatchPath; #define TYPE_WATCH_PATH (watch_path_get_type()) #define WATCH_PATH( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WATCH_PATH, WatchPath )) #define WATCH_PATH_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WATCH_PATH, WatchPathClass)) #define IS_WATCH_PATH( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WATCH_PATH )) #define IS_WATCH_PATH_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WATCH_PATH )) #define WATCH_PATH_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WATCH_PATH, WatchPathClass )) struct _WatchPath { Watch parent_class; GSList *value; }; typedef struct _WatchPathClass { WatchClass parent_class; } WatchPathClass; GtkType watch_path_get_type( void ); GSList *watch_path_get( Watchgroup *, const char *name, GSList *fallback ); typedef struct _WatchBool WatchBool; #define TYPE_WATCH_BOOL (watch_bool_get_type()) #define WATCH_BOOL( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WATCH_BOOL, WatchBool )) #define WATCH_BOOL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WATCH_BOOL, WatchBoolClass)) #define IS_WATCH_BOOL( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WATCH_BOOL )) #define IS_WATCH_BOOL_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WATCH_BOOL )) #define WATCH_BOOL_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WATCH_BOOL, WatchBoolClass )) struct _WatchBool { Watch parent_class; gboolean value; }; typedef struct _WatchBoolClass { WatchClass parent_class; } WatchBoolClass; GtkType watch_bool_get_type( void ); gboolean watch_bool_get( Watchgroup *, const char *name, gboolean fallback ); typedef struct _WatchString WatchString; #define TYPE_WATCH_STRING (watch_string_get_type()) #define WATCH_STRING( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WATCH_STRING, WatchString )) #define WATCH_STRING_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WATCH_STRING, WatchStringClass)) #define IS_WATCH_STRING( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WATCH_STRING )) #define IS_WATCH_STRING_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WATCH_STRING )) #define WATCH_STRING_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WATCH_STRING, \ WatchStringClass )) struct _WatchString { Watch parent_class; char *value; }; typedef struct _WatchStringClass { WatchClass parent_class; } WatchStringClass; GtkType watch_string_get_type( void ); const char *watch_string_get( Watchgroup *, const char *name, const char *fallback ); /* Prefs we follow from C. */ /* Default show states. */ #define DISPLAY_RULERS \ (watch_bool_get( main_watchgroup, "DISPLAY_RULERS", FALSE )) #define DISPLAY_STATUS \ (watch_bool_get( main_watchgroup, "DISPLAY_STATUS", FALSE )) #define DISPLAY_CONVERSION \ (watch_bool_get( main_watchgroup, "DISPLAY_CONVERSION", FALSE )) /* Display a crosshair on image windows ... turn-off-able, since some desktop * themes have almost invisible crosshairs. */ #define DISPLAY_CROSSHAIR \ (watch_bool_get( main_watchgroup, "DISPLAY_CROSSHAIR", TRUE )) /* Update children during paint. */ #define PAINTBOX_RECOMP \ (watch_bool_get( main_watchgroup, "PAINTBOX_RECOMP", TRUE )) /* Help browser. */ #define BOX_BROWSER (watch_string_get( main_watchgroup, "BROWSER", "mozilla" )) #define BOX_BROWSER_REMOTE (watch_string_get( main_watchgroup, \ "BROWSER_REMOTE", "-remote 'openURL(%s)'" )) /* Thumbnail size. */ #define DISPLAY_THUMBNAIL \ (watch_int_get( main_watchgroup, "DISPLAY_THUMBNAIL", 64 )) /* High-quality thumbnails. */ #define DISPLAY_THUMBNAIL_HQ \ (watch_bool_get( main_watchgroup, "DISPLAY_THUMBNAIL_HQ", FALSE )) /* File stuff. */ #define PIN_FILESEL \ (watch_bool_get( main_watchgroup, "CALC_PIN_FILESEL", FALSE )) #define IP_JPEG_Q \ (watch_int_get( main_watchgroup, "JPEG_Q", 75 )) #define IP_JPEG_ICC_PROFILE \ (watch_int_get( main_watchgroup, "JPEG_ICC_PROFILE", 0 )) #define IP_JPEG_ICC_PROFILE_FILE \ (watch_string_get( main_watchgroup, "JPEG_ICC_PROFILE_FILE", \ "$VIPSHOME/share/nip2/data/sRGB.icm" )) #define IP_PPM_MODE \ (watch_int_get( main_watchgroup, "PPM_MODE", 0 )) #define IP_CSV_SEPARATOR \ (watch_string_get( main_watchgroup, "CSV_SEPARATOR", "\t" )) #define IP_PNG_COMPRESSION \ (watch_int_get( main_watchgroup, "PNG_COMPRESSION", 6 )) #define IP_PNG_INTERLACE \ (watch_int_get( main_watchgroup, "PNG_INTERLACE", 0 )) #define IP_TIFF_COMPRESSION \ (watch_int_get( main_watchgroup, "TIFF_COMPRESSION", 0 )) #define IP_TIFF_JPEG_Q \ (watch_int_get( main_watchgroup, "TIFF_JPEG_Q", 75 )) #define IP_TIFF_LAYOUT \ (watch_int_get( main_watchgroup, "TIFF_LAYOUT", 0 )) #define IP_TIFF_TILE_WIDTH \ (watch_int_get( main_watchgroup, "TIFF_TILE_WIDTH", 128 )) #define IP_TIFF_TILE_HEIGHT \ (watch_int_get( main_watchgroup, "TIFF_TILE_HEIGHT", 128 )) #define IP_TIFF_MULTI_RES \ (watch_int_get( main_watchgroup, "TIFF_MULTI_RES", 0 )) #define IP_TIFF_FORMAT \ (watch_int_get( main_watchgroup, "TIFF_FORMAT", 0 )) #define IP_TIFF_PREDICTOR \ (watch_int_get( main_watchgroup, "TIFF_PREDICTOR", 0 )) #define IP_TIFF_BIGTIFF \ (watch_bool_get( main_watchgroup, "TIFF_BIGTIFF", FALSE )) /* Autoreload. */ #define CALC_RELOAD (watch_bool_get( main_watchgroup, "CALC_RELOAD", FALSE )) /* Max chars we print. */ #define LINELENGTH \ IM_CLIP( 10, \ watch_int_get( main_watchgroup, "CALC_LINELENGTH", 80 ), \ MAX_LINELENGTH ) /* CPUs we work over. */ #define VIPS_CPUS \ (watch_int_get( main_watchgroup, "VIPS_CPUS", 1 )) /* Bar prefs. */ #define MAINW_TOOLBAR \ (watch_bool_get( main_watchgroup, "MAINW_TOOLBAR", TRUE )) #define MAINW_TOOLBAR_STYLE \ (watch_int_get( main_watchgroup, "MAINW_TOOLBAR_STYLE", 0 )) #define MAINW_STATUSBAR \ (watch_bool_get( main_watchgroup, "MAINW_STATUSBAR", TRUE )) #define WORKSPACE_LPANE_OPEN \ (watch_bool_get( main_watchgroup, "WORKSPACE_LPANE_OPEN", FALSE )) #define WORKSPACE_LPANE_POSITION \ (watch_int_get( main_watchgroup, "WORKSPACE_LPANE_POSITION", 200 )) #define WORKSPACE_RPANE_OPEN \ (watch_bool_get( main_watchgroup, "WORKSPACE_RPANE_OPEN", FALSE )) #define WORKSPACE_RPANE_POSITION \ (watch_int_get( main_watchgroup, "WORKSPACE_RPANE_POSITION", 400 )) /* Heap size. Big enough to always load prefs, small enough that it doesn't * trash the computer. */ #define MAX_HEAPSIZE \ IM_CLIP( 100000, \ watch_int_get( main_watchgroup, "CALC_MAX_HEAP", 200000 ), \ 10000000 ) /* Region dragging. */ #ifdef NO_UPDATE #define CALC_RECOMP_REGION \ (watch_bool_get( main_watchgroup, "CALC_RECOMP_REGION", FALSE )) #else #define CALC_RECOMP_REGION \ (watch_bool_get( main_watchgroup, "CALC_RECOMP_REGION", TRUE )) #endif /* Slider dragging. */ #define CALC_RECOMP_SLIDER \ (watch_bool_get( main_watchgroup, "CALC_RECOMP_SLIDER", FALSE )) /* Popup new objects. */ #define POPUP_NEW_ROWS \ (watch_bool_get( main_watchgroup, "POPUP_NEW_ROWS", FALSE )) /* Draw LEDs rather than recolouring tally buttons. */ #define CALC_DISPLAY_LED \ (watch_bool_get( main_watchgroup, "CALC_DISPLAY_LED", FALSE )) /* Number of vips calls to memoise. */ #define CALL_HISTORY_MAX \ (watch_int_get( main_watchgroup, "VIPS_HISTORY_MAX", 200 )) /* Auto save wses. */ #define AUTO_WS_SAVE \ (watch_bool_get( main_watchgroup, "CALC_AUTO_WS_SAVE", TRUE )) /* Image window geometry. */ #define IMAGE_WINDOW_WIDTH \ (watch_int_get( main_watchgroup, "IMAGE_WINDOW_WIDTH", 600 )) #define IMAGE_WINDOW_HEIGHT \ (watch_int_get( main_watchgroup, "IMAGE_WINDOW_HEIGHT", 650 )) /* Default font. */ #define PAINTBOX_FONT \ (watch_string_get( main_watchgroup, "PAINTBOX_FONT", "Sans 12" )) /* Max undo steps ... -1 == unlimited. */ #define PAINTBOX_MAX_UNDO \ (watch_int_get( main_watchgroup, "PAINTBOX_MAX_UNDO", -1 )) /* Default image file type. */ #define IMAGE_FILE_TYPE \ (watch_int_get( main_watchgroup, "IMAGE_FILE_TYPE", 0 )) /* Prefs we watch. */ #define PATH_SEARCH (watch_path_get( main_watchgroup, "CALC_PATH_SEARCH", \ path_search_default )) #define PATH_START (watch_path_get( main_watchgroup, "CALC_PATH_START", \ path_start_default )) #define PATH_TMP (watch_string_get( main_watchgroup, "CALC_PATH_TMP", \ path_tmp_default )) /* How we print stuff. */ #define TRACE_FUNCTIONS \ (watch_bool_get( main_watchgroup, "CALC_TRACE_FUNCTIONS", FALSE )) #define PRINT_CARTESIAN \ (watch_bool_get( main_watchgroup, "CALC_PRINT_CARTIESIAN", FALSE )) /* Program window. */ #define PROGRAM_PANE_POSITION \ (watch_double_get( main_watchgroup, "PROGRAM_PANE_POSITION", 200 )) ================================================ FILE: src/workspace.c ================================================ /* Manage workspace 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_VERBOSE #define DEBUG */ #include "ip.h" static ModelClass *parent_class = NULL; static GSList *workspace_all = NULL; static GSList *workspace_needs_layout = NULL; void workspace_set_needs_layout( Workspace *ws, gboolean needs_layout ) { #ifdef DEBUG_VERBOSE printf( "workspace_set_needs_layout: %p %s %d\n", ws, NN( IOBJECT( ws )->name ), needs_layout ); #endif /*DEBUG_VERBOSE*/ if( !ws->needs_layout && needs_layout && !ws->in_dispose ) { g_assert( !g_slist_find( workspace_needs_layout, ws ) ); ws->needs_layout = TRUE; workspace_needs_layout = g_slist_prepend( workspace_needs_layout, ws ); } if( ws->needs_layout && !needs_layout ) { g_assert( g_slist_find( workspace_needs_layout, ws ) ); ws->needs_layout = FALSE; workspace_needs_layout = g_slist_remove( workspace_needs_layout, ws ); } } GSList * workspace_get_needs_layout() { return( workspace_needs_layout ); } Workspacegroup * workspace_get_workspacegroup( Workspace *ws ) { iContainer *parent; if( (parent = ICONTAINER( ws )->parent) ) return( WORKSPACEGROUP( parent ) ); return( NULL ); } Workspaceroot * workspace_get_workspaceroot( Workspace *ws ) { return( workspace_get_workspacegroup( ws )->wsr ); } void workspace_set_modified( Workspace *ws, gboolean modified ) { Workspacegroup *wsg; if( (wsg = workspace_get_workspacegroup( ws )) ) filemodel_set_modified( FILEMODEL( wsg ), modified ); } static void * workspace_map_sub( Workspacegroup *wsg, workspace_map_fn fn, void *a, void *b ) { g_assert( IS_WORKSPACEGROUP( wsg ) ); return( icontainer_map( ICONTAINER( wsg ), (icontainer_map_fn) fn, a, b ) ); } /* Over all workspaces. */ void * workspace_map( workspace_map_fn fn, void *a, void *b ) { return( icontainer_map3( ICONTAINER( main_workspaceroot ), (icontainer_map3_fn) workspace_map_sub, fn, a, b ) ); } /* Map across the columns in a workspace. */ void * workspace_map_column( Workspace *ws, column_map_fn fn, void *a ) { return( icontainer_map( ICONTAINER( ws ), (icontainer_map_fn) fn, a, NULL ) ); } /* Map across a Workspace, applying to the symbols of the top-level rows. */ void * workspace_map_symbol( Workspace *ws, symbol_map_fn fn, void *a ) { return( icontainer_map( ICONTAINER( ws ), (icontainer_map_fn) column_map_symbol, (void *) fn, a ) ); } static void * workspace_is_empty_sub( Symbol *sym ) { return( sym ); } /* Does a workspace contain no rows? */ gboolean workspace_is_empty( Workspace *ws ) { return( workspace_map_symbol( ws, (symbol_map_fn) workspace_is_empty_sub, NULL ) == NULL ); } /* Map a function over all selected rows in a workspace. */ void * workspace_selected_map( Workspace *ws, row_map_fn fn, void *a, void *b ) { return( slist_map2( ws->selected, (SListMap2Fn) fn, a, b ) ); } static void * workspace_selected_map_sym_sub( Row *row, symbol_map_fn fn, void *a ) { return( fn( row->sym, a, NULL, NULL ) ); } /* Map a function over all selected symbols in a workspace. */ void * workspace_selected_map_sym( Workspace *ws, symbol_map_fn fn, void *a, void *b ) { return( workspace_selected_map( ws, (row_map_fn) workspace_selected_map_sym_sub, (void *) fn, a ) ); } /* Are there any selected rows? */ gboolean workspace_selected_any( Workspace *ws ) { return( ws->selected != NULL ); } /* Number of selected rows. */ int workspace_selected_num( Workspace *ws ) { return( g_slist_length( ws->selected ) ); } static void * workspace_selected_sym_sub( Row *row, Symbol *sym ) { if( row->sym == sym ) return( row ); return( NULL ); } /* Is sym selected? */ gboolean workspace_selected_sym( Workspace *ws, Symbol *sym ) { return( workspace_selected_map( ws, (row_map_fn) workspace_selected_sym_sub, sym, NULL ) != NULL ); } /* Is just one row selected? If yes, return it. */ Row * workspace_selected_one( Workspace *ws ) { int len = g_slist_length( ws->selected ); if( len == 1 ) return( (Row *)(ws->selected->data) ); else if( len == 0 ) { error_top( _( "No objects selected." ) ); error_sub( _( "Select exactly one object and try again." ) ); return( NULL ); } else { error_top( _( "More than one object selected." ) ); error_sub( _( "Select exactly one object and try again." ) ); return( NULL ); } } static void * workspace_deselect_all_sub( Column *col ) { col->last_select = NULL; return( NULL ); } /* Deselect all rows. */ void workspace_deselect_all( Workspace *ws ) { (void) workspace_selected_map( ws, (row_map_fn) row_deselect, NULL, NULL ); (void) workspace_map_column( ws, (column_map_fn) workspace_deselect_all_sub, NULL ); } /* Track this while we build a names list. */ typedef struct { VipsBuf *buf; const char *separator; gboolean first; } NamesInfo; /* Add a name to a string for a symbol. */ static void * workspace_selected_names_sub( Row *row, NamesInfo *names ) { if( !names->first ) vips_buf_appends( names->buf, names->separator ); /* Hack: if this is a matrix with selected cells, use an extract to * get those cells out. We should really have a row method for this I * guess :-( */ if( row->child_rhs && row->child_rhs->graphic && IS_MATRIX( row->child_rhs->graphic ) && MATRIX( row->child_rhs->graphic )->selected ) { Matrix *matrix = MATRIX( row->child_rhs->graphic ); vips_buf_appends( names->buf, "(" ); row_qualified_name( row, names->buf ); vips_buf_appendf( names->buf, ".extract %d %d %d %d)", matrix->range.left, matrix->range.top, matrix->range.width, matrix->range.height ); } else row_qualified_name( row, names->buf ); names->first = FALSE; return( NULL ); } /* Add a list of selected symbol names to a string. */ void workspace_selected_names( Workspace *ws, VipsBuf *buf, const char *separator ) { NamesInfo names; names.buf = buf; names.separator = separator; names.first = TRUE; (void) workspace_selected_map( ws, (row_map_fn) workspace_selected_names_sub, &names, NULL ); } void workspace_column_names( Column *col, VipsBuf *buf, const char *separator ) { NamesInfo names; names.buf = buf; names.separator = separator; names.first = TRUE; (void) column_map( col, (row_map_fn) workspace_selected_names_sub, &names, NULL ); } /* Select all objects in all columns. */ void workspace_select_all( Workspace *ws ) { (void) icontainer_map( ICONTAINER( ws ), (icontainer_map_fn) column_select_symbols, NULL, NULL ); } /* Is there just one column, and is it empty? */ Column * workspace_is_one_empty( Workspace *ws ) { GSList *children = ICONTAINER( ws )->children; Column *col; if( g_slist_length( children ) != 1 ) return( NULL ); col = COLUMN( children->data ); if( !column_is_empty( col ) ) return( NULL ); return( col ); } /* Search for a column by name. */ Column * workspace_column_find( Workspace *ws, const char *name ) { Model *model; if( !(model = icontainer_map( ICONTAINER( ws ), (icontainer_map_fn) iobject_test_name, (void *) name, NULL )) ) return( NULL ); return( COLUMN( model ) ); } /* Return the column for a name ... an existing column, or a new one. */ Column * workspace_column_get( Workspace *ws, const char *name ) { Column *col; /* Exists? */ if( (col = workspace_column_find( ws, name )) ) return( col ); /* No - build new column and return a pointer to that. */ return( column_new( ws, name ) ); } /* Make up a new column name. Check for not already in workspace. */ void workspace_column_name_new( Workspace *ws, char *name ) { do { number_to_string( ws->next++, name ); } while( workspace_column_find( ws, name ) ); } Column * workspace_get_column( Workspace *ws ) { if( ICONTAINER( ws )->current ) return( COLUMN( ICONTAINER( ws )->current ) ); return( NULL ); } /* Select a column. Can select NULL for no current col in this ws. */ void workspace_column_select( Workspace *ws, Column *col ) { icontainer_current( ICONTAINER( ws ), ICONTAINER( col ) ); } /* Make sure we have a column selected ... pick one of the existing columns; if * there are none, make a column. */ Column * workspace_column_pick( Workspace *ws ) { Column *col; if( (col = workspace_get_column( ws )) ) return( col ); if( (col = COLUMN( icontainer_get_nth_child( ICONTAINER( ws ), 0 ) )) ) { workspace_column_select( ws, col ); return( col ); } /* Make an empty column ... always at the top left. */ col = column_new( ws, "A" ); col->x = WORKSPACEVIEW_MARGIN_LEFT; col->y = WORKSPACEVIEW_MARGIN_TOP; workspace_column_select( ws, col ); return( col ); } /* Make and select a column. Used for "new column" UI actions. */ Column * workspace_column_new( Workspace *ws ) { char new_name[MAX_STRSIZE]; Column *old_col; Column *col; workspace_column_name_new( ws, new_name ); if( !(col = column_new( ws, new_name )) ) return( NULL ); /* Position just to right of currently selected column. */ if( (old_col = workspace_get_column( ws )) ) { col->x = old_col->x + 50; col->y = old_col->y; } workspace_column_select( ws, col ); column_scrollto( col, MODEL_SCROLL_TOP ); return( col ); } /* Make a new symbol, part of the current column. */ static Symbol * workspace_add_symbol( Workspace *ws ) { Column *col = workspace_column_pick( ws ); Symbol *sym; char *name; name = column_name_new( col ); sym = symbol_new( ws->sym->expr->compile, name ); IM_FREE( name ); return( sym ); } /* Make up a new definition. */ Symbol * workspace_add_def( Workspace *ws, const char *str ) { Column *col = workspace_column_pick( ws ); Symbol *sym; char *name; #ifdef DEBUG printf( "workspace_add_def: %s\n", str ); #endif /*DEBUG*/ if( !str || strspn( str, WHITESPACE ) == strlen( str ) ) return( NULL ); /* Try parsing as a "fred = 12" style def. */ attach_input_string( str ); if( (name = parse_test_define()) ) { sym = symbol_new( ws->sym->expr->compile, name ); IM_FREE( name ); attach_input_string( str + IM_CLIP( 0, input_state.charpos - 1, strlen( str ) ) ); } else { /* That didn't work. Make a sym from the col name. */ sym = workspace_add_symbol( ws ); attach_input_string( str ); } if( !symbol_user_init( sym ) || !parse_rhs( sym->expr, PARSE_RHS ) ) { /* Another parse error. */ expr_error_get( sym->expr ); /* Block changes to error_string ... symbol_destroy() * can set this for compound objects. */ error_block(); IDESTROY( sym ); error_unblock(); return( NULL ); } /* If we're redefining a sym, it might have a row already. */ if( !sym->expr->row ) (void) row_new( col->scol, sym, &sym->expr->root ); symbol_made( sym ); workspace_set_modified( ws, TRUE ); return( sym ); } /* Make up a new definition, recalc and scroll to make it visible. */ Symbol * workspace_add_def_recalc( Workspace *ws, const char *str ) { Column *col = workspace_column_pick( ws ); Symbol *sym; #ifdef DEBUG printf( "workspace_add_def_recalc: %s\n", str ); #endif /*DEBUG*/ if( !(sym = workspace_add_def( ws, str )) ) return( NULL ); if( !symbol_recalculate_check( sym ) ) { /* Eval error. */ expr_error_get( sym->expr ); error_block(); IDESTROY( sym ); error_unblock(); return( NULL ); } /* Jump to column containing object. */ column_scrollto( col, MODEL_SCROLL_BOTTOM ); return( sym ); } gboolean workspace_load_file_buf( VipsBuf *buf, const char *filename ) { if( callv_string_filenamef( (callv_string_fn) vips_format_for_file, "%s", filename ) ) vips_buf_appends( buf, "Image_file" ); else vips_buf_appends( buf, "Matrix_file" ); vips_buf_appends( buf, " \"" ); vips_buf_appendsc( buf, TRUE, filename ); vips_buf_appends( buf, "\"" ); return( TRUE ); } /* Load a matrix or image. Don't recalc: you need to recalc later to test for * success/fail. See eg. workspace_add_def_recalc() */ Symbol * workspace_load_file( Workspace *ws, const char *filename ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); Symbol *sym; if( !workspace_load_file_buf( &buf, filename ) ) return( NULL ); if( !(sym = workspace_add_def( ws, vips_buf_all( &buf ) )) ) return( NULL ); mainw_recent_add( &mainw_recent_image, filename ); return( sym ); } static void workspace_dispose( GObject *gobject ) { Workspace *ws; #ifdef DEBUG printf( "workspace_dispose: %p %s\n", gobject, NN( IOBJECT( gobject )->name ) ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WORKSPACE( gobject ) ); ws = WORKSPACE( gobject ); workspace_set_needs_layout( ws, FALSE ); ws->in_dispose = TRUE; UNREF( ws->kitg ); UNREF( ws->local_kitg ); IDESTROY( ws->sym ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void workspace_finalize( GObject *gobject ) { Workspace *ws; #ifdef DEBUG printf( "workspace_finalize: %p %s\n", gobject, NN( IOBJECT( gobject )->name ) ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WORKSPACE( gobject ) ); ws = WORKSPACE( gobject ); IM_FREE( ws->status ); IM_FREE( ws->local_defs ); workspace_all = g_slist_remove( workspace_all, ws ); G_OBJECT_CLASS( parent_class )->finalize( gobject ); } static void workspace_changed( iObject *iobject ) { Workspace *ws; Workspacegroup *wsg; #ifdef DEBUG_VERBOSE printf( "workspace_changed: %s\n", NN( iobject->name ) ); #endif /*DEBUG_VERBOSE*/ g_return_if_fail( iobject != NULL ); g_return_if_fail( IS_WORKSPACE( iobject ) ); ws = WORKSPACE( iobject ); wsg = workspace_get_workspacegroup( ws ); /* Signal changed on our workspacegroup, if we're the current object. */ if( wsg && ICONTAINER( wsg )->current == ICONTAINER( iobject ) ) iobject_changed( IOBJECT( wsg ) ); IOBJECT_CLASS( parent_class )->changed( iobject ); } static void workspace_child_add( iContainer *parent, iContainer *child, int pos ) { Workspace *ws = WORKSPACE( parent ); Column *col = COLUMN( child ); ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos ); if( col->selected ) workspace_column_select( ws, col ); } static void workspace_child_remove( iContainer *parent, iContainer *child ) { Workspace *ws = WORKSPACE( parent ); workspace_set_modified( ws, TRUE ); ICONTAINER_CLASS( parent_class )->child_remove( parent, child ); } static void workspace_current( iContainer *parent, iContainer *child ) { Workspace *ws = WORKSPACE( parent ); Column *col = COLUMN( child ); Column *current = workspace_get_column( ws ); if( current ) current->selected = FALSE; if( col ) col->selected = TRUE; ICONTAINER_CLASS( parent_class )->current( parent, child ); } static void workspace_link( Workspace *ws, Workspacegroup *wsg, const char *name ) { Workspaceroot *wsr = wsg->wsr; Symbol *sym; #ifdef DEBUG printf( "workspace_link: naming ws %p as %s\n", ws, name ); #endif /*DEBUG*/ sym = symbol_new_defining( wsr->sym->expr->compile, name ); ws->sym = sym; sym->type = SYM_WORKSPACE; sym->ws = ws; sym->expr = expr_new( sym ); (void) compile_new( sym->expr ); symbol_made( sym ); iobject_set( IOBJECT( ws ), name, NULL ); ws->local_kitg = toolkitgroup_new( ws->sym ); g_object_ref( G_OBJECT( ws->local_kitg ) ); iobject_sink( IOBJECT( ws->local_kitg ) ); } static const char * workspacemode_to_char( WorkspaceMode mode ) { switch( mode ) { case WORKSPACE_MODE_REGULAR: return( "WORKSPACE_MODE_REGULAR" ); case WORKSPACE_MODE_FORMULA: return( "WORKSPACE_MODE_FORMULA" ); case WORKSPACE_MODE_NOEDIT: return( "WORKSPACE_MODE_NOEDIT" ); default: return( NULL ); } } static WorkspaceMode char_to_workspacemode( const char *mode ) { if( strcasecmp( mode, "WORKSPACE_MODE_REGULAR" ) == 0 ) return( WORKSPACE_MODE_REGULAR ); else if( strcasecmp( mode, "WORKSPACE_MODE_FORMULA" ) == 0 ) return( WORKSPACE_MODE_FORMULA ); else if( strcasecmp( mode, "WORKSPACE_MODE_NOEDIT" ) == 0 ) return( WORKSPACE_MODE_NOEDIT ); else return( (WorkspaceMode) -1 ); } static View * workspace_view_new( Model *model, View *parent ) { return( workspaceview_new() ); } static gboolean workspace_load( Model *model, ModelLoadState *state, Model *parent, xmlNode *xnode ) { Workspace *ws = WORKSPACE( model ); char buf[FILENAME_MAX]; char *txt; g_assert( IS_WORKSPACEGROUP( parent ) ); /* "view" is optional, for backwards compatibility. */ if( get_sprop( xnode, "view", buf, FILENAME_MAX ) ) { WorkspaceMode mode = char_to_workspacemode( buf ); if( (int) mode >= 0 ) /* Could call workspace_set_mode(), but this is only a * load, so so what. */ ws->mode = mode; } /* Also optional. */ (void) get_dprop( xnode, "scale", &ws->scale ); (void) get_dprop( xnode, "offset", &ws->offset ); (void) get_bprop( xnode, "locked", &ws->locked ); (void) get_bprop( xnode, "lpane_open", &ws->lpane_open ); (void) get_iprop( xnode, "lpane_position", &ws->lpane_position ); (void) get_bprop( xnode, "rpane_open", &ws->rpane_open ); (void) get_iprop( xnode, "rpane_position", &ws->rpane_position ); if( get_sprop( xnode, "name", buf, FILENAME_MAX ) ) { IM_SETSTR( IOBJECT( ws )->name, buf ); } if( get_sprop( xnode, "caption", buf, FILENAME_MAX ) ) { IM_SETSTR( IOBJECT( ws )->caption, buf ); } /* Don't use get_sprop() and avoid a limit on def size. */ if( (txt = (char *) xmlGetProp( xnode, (xmlChar *) "local_defs" )) ) { (void) workspace_local_set( ws, txt ); IM_FREEF( xmlFree, txt ); } (void) get_iprop( xnode, "major", &ws->major ); (void) get_iprop( xnode, "minor", &ws->minor ); if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) ) return( FALSE ); return( TRUE ); } static xmlNode * workspace_save( Model *model, xmlNode *xnode ) { Workspace *ws = WORKSPACE( model ); Workspacegroup *wsg = workspace_get_workspacegroup( ws ); xmlNode *xthis; if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) ) return( NULL ); if( !set_sprop( xthis, "view", workspacemode_to_char( ws->mode ) ) || !set_dprop( xthis, "scale", ws->scale ) || !set_dprop( xthis, "offset", ws->offset ) || !set_sprop( xthis, "locked", bool_to_char( ws->locked ) ) || !set_iprop( xthis, "lpane_position", ws->lpane_position ) || !set_sprop( xthis, "lpane_open", bool_to_char( ws->lpane_open ) ) || !set_iprop( xthis, "rpane_position", ws->rpane_position ) || !set_sprop( xthis, "rpane_open", bool_to_char( ws->rpane_open ) ) || !set_sprop( xthis, "local_defs", ws->local_defs ) || !set_sprop( xthis, "name", IOBJECT( ws )->name ) || !set_sprop( xthis, "caption", IOBJECT( ws )->caption ) ) return( NULL ); /* We have to save our workspacegroup's filename here for compt with * older nip2. */ if( !set_sprop( xthis, "filename", FILEMODEL( wsg )->filename ) ) return( NULL ); if( !set_iprop( xthis, "major", ws->major ) || !set_iprop( xthis, "minor", ws->minor ) ) return( NULL ); return( xthis ); } static void workspace_empty( Model *model ) { Workspace *ws = WORKSPACE( model ); /* Make sure this gets reset. */ ws->area.left = 0; ws->area.top = 0; ws->area.width = 0; ws->area.height = 0; MODEL_CLASS( parent_class )->empty( model ); } static void * workspace_load_toolkit( const char *filename, Toolkitgroup *toolkitgroup ) { if( !toolkit_new_from_file( toolkitgroup, filename ) ) iwindow_alert( NULL, GTK_MESSAGE_ERROR ); return( NULL ); } /* The compat modes this version of nip2 has. Search the compat dir and make a * list of these things. */ #define MAX_COMPAT (100) static int compat_major[MAX_COMPAT]; static int compat_minor[MAX_COMPAT]; static int n_compat = 0; static void * workspace_build_compat_fn( const char *filename ) { char *basename; int major; int minor; basename = g_path_get_basename( filename ); if( sscanf( basename, "%d.%d", &major, &minor ) != 2 ) { g_free( basename ); return( NULL ); } g_free( basename ); compat_major[n_compat] = major; compat_minor[n_compat] = minor; n_compat += 1; #ifdef DEBUG printf( "workspace_build_compat_fn: found major = %d, minor = %d\n", major, minor ); #endif /*DEBUG*/ return( NULL ); } /* Build the list of ws compatibility defs we have. */ static void workspace_build_compat( void ) { if( n_compat > 0 ) return; path_map_dir( "$VIPSHOME/share/" PACKAGE "/compat", "*.*", (path_map_fn) workspace_build_compat_fn, NULL ); } /* Given a major/minor (eg. read from a ws header), return non-zero if we have * a set of compat defs. */ int workspace_have_compat( int major, int minor, int *best_major, int *best_minor ) { int i; int best; #ifdef DEBUG printf( "workspace_have_compat: searching for %d.%d\n", major, minor ); #endif /*DEBUG*/ /* Sets of ws compatibility defs cover themselves and any earlier * releases, as far back as the next set of compat defs. We need to * search for the smallest compat version that's greater than the * version number in the file. */ workspace_build_compat(); best = -1; for( i = 0; i < n_compat; i++ ) if( major <= compat_major[i] && minor <= compat_minor[i] ) /* Found a possible compat set, is it better than the * best we've seen so far? */ if( best == -1 || compat_major[i] < compat_major[best] || compat_minor[i] < compat_minor[best] ) best = i; if( best == -1 ) return( 0 ); #ifdef DEBUG printf( "\tfound %d.%d\n", compat_major[best], compat_minor[best] ); #endif /*DEBUG*/ if( best_major ) *best_major = compat_major[best]; if( best_minor ) *best_minor = compat_minor[best]; return( 1 ); } void workspace_get_version( Workspace *ws, int *major, int *minor ) { *major = ws->major; *minor = ws->minor; } gboolean workspace_load_compat( Workspace *ws, int major, int minor ) { char pathname[FILENAME_MAX]; GSList *path; int best_major; int best_minor; if( workspace_have_compat( major, minor, &best_major, &best_minor ) ) { /* Make a private toolkitgroup local to this workspace to * hold the compatibility defs we are planning to load. */ UNREF( ws->kitg ); ws->kitg = toolkitgroup_new( ws->sym ); g_object_ref( G_OBJECT( ws->kitg ) ); iobject_sink( IOBJECT( ws->kitg ) ); im_snprintf( pathname, FILENAME_MAX, "$VIPSHOME/share/" PACKAGE "/compat/%d.%d", best_major, best_minor ); path = path_parse( pathname ); if( path_map( path, "*.def", (path_map_fn) workspace_load_toolkit, ws->kitg ) ) { path_free2( path ); return( FALSE ); } path_free2( path ); #ifdef DEBUG printf( "workspace_load_compat: loaded %d.%d\n", best_major, best_minor ); #endif /*DEBUG*/ ws->compat_major = best_major; ws->compat_minor = best_minor; } else { #ifdef DEBUG printf( "workspace_load_compat: no compat necessary\n" ); #endif /*DEBUG*/ /* No compat defs necessary for this ws. */ ws->compat_major = 0; ws->compat_minor = 0; } return( TRUE ); } static void workspace_class_init( WorkspaceClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); iObjectClass *iobject_class = IOBJECT_CLASS( class ); iContainerClass *icontainer_class = (iContainerClass *) class; ModelClass *model_class = (ModelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = workspace_dispose; gobject_class->finalize = workspace_finalize; iobject_class->changed = workspace_changed; iobject_class->user_name = _( "Tab" ); icontainer_class->child_add = workspace_child_add; icontainer_class->child_remove = workspace_child_remove; icontainer_class->current = workspace_current; model_class->view_new = workspace_view_new; model_class->load = workspace_load; model_class->save = workspace_save; model_class->empty = workspace_empty; /* Static init. */ model_register_loadable( MODEL_CLASS( class ) ); } static void workspace_init( Workspace *ws ) { ws->sym = NULL; /* We default to using the main toolkitgroup for our definitions. * Unref and load private defs if we need compatibility. */ ws->kitg = main_toolkitgroup; g_object_ref( G_OBJECT( ws->kitg ) ); ws->next = 0; ws->selected = NULL; ws->errors = NULL; ws->mode = WORKSPACE_MODE_REGULAR; ws->major = MAJOR_VERSION; ws->minor = MINOR_VERSION; ws->compat_major = 0; ws->compat_minor = 0; ws->area.left = 0; ws->area.top = 0; ws->area.width = 0; ws->area.height = 0; ws->vp = ws->area; ws->lpane_open = WORKSPACE_LPANE_OPEN; ws->lpane_position = WORKSPACE_LPANE_POSITION; ws->rpane_open = WORKSPACE_RPANE_OPEN; ws->rpane_position = WORKSPACE_RPANE_POSITION; ws->status = NULL; ws->scale = 1.0; ws->offset = 0.0; ws->local_defs = im_strdupn( _( "// private definitions for this tab\n" ) ); ws->local_kitg = NULL; ws->local_kit = NULL; workspace_all = g_slist_prepend( workspace_all, ws ); } GType workspace_get_type( void ) { static GType workspace_type = 0; if( !workspace_type ) { static const GTypeInfo info = { sizeof( WorkspaceClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) workspace_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Workspace ), 32, /* n_preallocs */ (GInstanceInitFunc) workspace_init, }; workspace_type = g_type_register_static( TYPE_MODEL, "Workspace", &info, 0 ); } return( workspace_type ); } Workspace * workspace_new( Workspacegroup *wsg, const char *name ) { Workspaceroot *wsr = wsg->wsr; Workspace *ws; #ifdef DEBUG printf( "workspace_new: %s\n", name ); #endif /*DEBUG*/ if( compile_lookup( wsr->sym->expr->compile, name ) ) { error_top( _( "Name clash." ) ); error_sub( _( "Can't create workspace \"%s\". " "A symbol with that name already exists." ), name ); return( NULL ); } ws = WORKSPACE( g_object_new( TYPE_WORKSPACE, NULL ) ); workspace_link( ws, wsg, name ); icontainer_child_add( ICONTAINER( wsg ), ICONTAINER( ws ), -1 ); return( ws ); } /* Make the blank workspace we present the user with (in the absence of * anything else). */ Workspace * workspace_new_blank( Workspacegroup *wsg ) { char name[256]; Workspace *ws; workspaceroot_name_new( wsg->wsr, name ); if( !(ws = workspace_new( wsg, name )) ) return( NULL ); /* Make an empty column. */ (void) workspace_column_pick( ws ); icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); iobject_set( IOBJECT( ws ), NULL, _( "Default empty tab" ) ); return( ws ); } /* Get the bottom row from the current column. */ static Row * workspace_get_bottom( Workspace *ws ) { return( column_get_bottom( workspace_column_pick( ws ) ) ); } gboolean workspace_add_action( Workspace *ws, const char *name, const char *action, int nparam ) { Column *col = workspace_column_pick( ws ); char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); /* Are there any selected symbols? */ vips_buf_appends( &buf, action ); if( nparam > 0 && workspace_selected_any( ws ) ) { if( nparam != workspace_selected_num( ws ) ) { error_top( _( "Wrong number of arguments." ) ); error_sub( _( "%s needs %d arguments, " "there are %d selected." ), name, nparam, workspace_selected_num( ws ) ); return( FALSE ); } vips_buf_appends( &buf, " " ); workspace_selected_names( ws, &buf, " " ); if( vips_buf_is_full( &buf ) ) { error_top( _( "Overflow error." ) ); error_sub( _( "Too many names selected." ) ); return( FALSE ); } if( !workspace_add_def_recalc( ws, vips_buf_all( &buf ) ) ) return( FALSE ); workspace_deselect_all( ws ); } else { /* Try to use the previous n items in this column as the * arguments. */ if( !column_add_n_names( col, name, &buf, nparam ) || !workspace_add_def_recalc( ws, vips_buf_all( &buf ) ) ) return( FALSE ); } return( TRUE ); } int workspace_number( void ) { return( g_slist_length( workspace_all ) ); } static void * workspace_row_dirty( Row *row, int serial ) { return( expr_dirty( row->expr, serial ) ); } /* Recalculate selected items. */ gboolean workspace_selected_recalc( Workspace *ws ) { if( workspace_selected_map( ws, (row_map_fn) workspace_row_dirty, GINT_TO_POINTER( link_serial_new() ), NULL ) ) return( FALSE ); /* Recalc even if autorecomp is off. */ symbol_recalculate_all_force( TRUE ); workspace_deselect_all( ws ); return( TRUE ); } static void * workspace_selected_remove2( Row *row ) { if( row != row->top_row ) return( row ); return( NULL ); } static void * workspace_selected_remove3( Row *row, int *nsel ) { if( row->selected ) *nsel += 1; return( NULL ); } static void * workspace_selected_remove4( Column *col, GSList **cs ) { int nsel = 0; (void) column_map( col, (row_map_fn) workspace_selected_remove3, &nsel, NULL ); if( nsel > 0 ) *cs = g_slist_prepend( *cs, col ); return( NULL ); } static void * workspace_selected_remove5( Column *col ) { Subcolumn *scol = col->scol; int nmembers = g_slist_length( ICONTAINER( scol )->children ); if( nmembers > 0 ) icontainer_pos_renumber( ICONTAINER( scol ) ); else IDESTROY( col ); return( NULL ); } /* Remove selected items. * * 0. check all objects to be destroyed are top level rows * 1. look for and note all columns containing items we are going to delete * 2. loop over selected items, and delete them one-by-one. * 3. loop over the columns we noted in 1 and delete empty ones * 4. renumber affected columns */ static gboolean workspace_selected_remove( Workspace *ws ) { Row *row; GSList *cs = NULL; if( (row = (Row *) workspace_selected_map( ws, (row_map_fn) workspace_selected_remove2, NULL, NULL )) ) { error_top( _( "You can only remove top level rows." ) ); error_sub( _( "Not all selected objects are top level " "rows." ) ); return( FALSE ); } (void) workspace_map_column( ws, (column_map_fn) workspace_selected_remove4, &cs ); (void) workspace_selected_map_sym( ws, (symbol_map_fn) iobject_destroy, NULL, NULL ); (void) slist_map( cs, (SListMapFn) workspace_selected_remove5, NULL ); IM_FREEF( g_slist_free, cs ); symbol_recalculate_all(); workspace_set_modified( ws, TRUE ); return( TRUE ); } /* Callback for workspace_selected_remove_yesno. Remove selected items. */ static void workspace_selected_remove_yesno_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Workspace *ws = WORKSPACE( client ); if( workspace_selected_remove( ws ) ) nfn( sys, IWINDOW_YES ); else nfn( sys, IWINDOW_ERROR ); } /* Ask before removing selected. */ void workspace_selected_remove_yesno( Workspace *ws, GtkWidget *parent ) { char txt[30]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( !workspace_selected_any( ws ) ) return; workspace_selected_names( ws, &buf, ", " ); box_yesno( parent, workspace_selected_remove_yesno_cb, iwindow_true_cb, ws, iwindow_notify_null, NULL, GTK_STOCK_DELETE, _( "Delete selected objects?" ), _( "Are you sure you want to delete %s?" ), vips_buf_all( &buf ) ); } /* Sub fn of below ... add a new index expression. */ static gboolean workspace_ungroup_add_index( Row *row, const char *fmt, int i ) { static char txt[200]; static VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_rewind( &buf ); row_qualified_name( row, &buf ); vips_buf_appendf( &buf, fmt, i ); if( !workspace_add_def_recalc( row->ws, vips_buf_all( &buf ) ) ) return( FALSE ); return( TRUE ); } static void * workspace_ungroup_row( Row *row ) { PElement *root = &row->expr->root; gboolean result; PElement value; int length; int i; if( !heap_is_instanceof( CLASS_GROUP, root, &result ) ) return( row ); if( result ) { if( !class_get_member( root, MEMBER_VALUE, NULL, &value ) || (length = heap_list_length_max( &value, 100 )) < 0 ) return( row ); for( i = 0; i < length; i++ ) if( !workspace_ungroup_add_index( row, ".value?%d", i ) ) return( row ); } else { if( !heap_is_list( root, &result ) ) return( row ); if( result ) { if( (length = heap_list_length_max( root, 100 )) < 0 ) return( row ); for( i = 0; i < length; i++ ) if( !workspace_ungroup_add_index( row, "?%d", i ) ) return( row ); } else { char txt[100]; VipsBuf buf = VIPS_BUF_STATIC( txt ); row_qualified_name( row, &buf ); error_top( _( "Unable to ungroup." ) ); error_sub( _( "Row \"%s\" is not a Group or a list." ), vips_buf_all( &buf ) ); return( row ); } } return( NULL ); } /* Ungroup the selected object(s), or the bottom object. */ gboolean workspace_selected_ungroup( Workspace *ws ) { if( !workspace_selected_any( ws ) ) { Row *row; if( (row = workspace_get_bottom( ws )) ) { if( workspace_ungroup_row( row ) ) return( FALSE ); symbol_recalculate_all(); } } else { /* Ungroup selected symbols. */ if( workspace_selected_map( ws, (row_map_fn) workspace_ungroup_row, NULL, NULL ) ) { symbol_recalculate_all(); return( FALSE ); } symbol_recalculate_all(); workspace_deselect_all( ws ); } return( TRUE ); } /* Group the selected object(s). */ gboolean workspace_selected_group( Workspace *ws ) { char txt[MAX_STRSIZE]; VipsBuf buf = VIPS_BUF_STATIC( txt ); if( !workspace_selected_any( ws ) ) { Row *row; if( (row = workspace_get_bottom( ws )) ) row_select( row ); } vips_buf_appends( &buf, "Group [" ); workspace_selected_names( ws, &buf, "," ); vips_buf_appends( &buf, "]" ); if( !workspace_add_def_recalc( ws, vips_buf_all( &buf ) ) ) return( FALSE ); workspace_deselect_all( ws ); return( TRUE ); } static Row * workspace_test_error( Row *row, Workspace *ws, int *found ) { g_assert( row->err ); /* Found next? */ if( *found ) return( row ); if( row == ws->last_error ) { /* Found the last one ... return the next one. */ *found = 1; return( NULL ); } return( NULL ); } /* FALSE for no errors. */ gboolean workspace_next_error( Workspace *ws ) { char txt[MAX_LINELENGTH]; VipsBuf buf = VIPS_BUF_STATIC( txt ); int found; if( !ws->errors ) return( FALSE ); /* Search for the one after the last one. */ found = 0; ws->last_error = (Row *) slist_map2( ws->errors, (SListMap2Fn) workspace_test_error, ws, &found ); /* NULL? We've hit end of table, start again. */ if( !ws->last_error ) { found = 1; ws->last_error = (Row *) slist_map2( ws->errors, (SListMap2Fn) workspace_test_error, ws, &found ); } /* *must* have one now. */ g_assert( ws->last_error && ws->last_error->err ); model_scrollto( MODEL( ws->last_error ), MODEL_SCROLL_TOP ); row_qualified_name( ws->last_error->expr->row, &buf ); vips_buf_appends( &buf, ": " ); vips_buf_appends( &buf, ws->last_error->expr->error_top ); workspace_set_status( ws, "%s", vips_buf_firstline( &buf ) ); return( TRUE ); } void workspace_set_status( Workspace *ws, const char *fmt, ... ) { va_list ap; char buf[256]; va_start( ap, fmt ); (void) im_vsnprintf( buf, 256, fmt, ap ); va_end( ap ); IM_SETSTR( ws->status, buf ); iobject_changed( IOBJECT( ws ) ); } void workspace_set_mode( Workspace *ws, WorkspaceMode mode ) { if( ws->mode != mode ) { ws->mode = mode; /* Rebuild all the views. Yuk! It would be better to get the * views that change with workspace mode to watch the * enclosing workspace and update on that. But we'd have * connections from almost every object in the ws. We don't * change mode very often, so just loop over them all. */ icontainer_map_all( ICONTAINER( ws ), (icontainer_map_fn) iobject_changed, NULL ); } } /* New ws private defs. */ gboolean workspace_local_set( Workspace *ws, const char *txt ) { /* New kit for defs ... will destroy any old defs, since we can't have * two kits with the same name. Don't register it, we don't want it * to be autosaved on quit. */ ws->local_kit = toolkit_new( ws->local_kitg, "Workspace Locals" ); filemodel_unregister( FILEMODEL( ws->local_kit ) ); IM_SETSTR( ws->local_defs, txt ); iobject_changed( IOBJECT( ws ) ); workspace_set_modified( ws, TRUE ); attach_input_string( txt ); if( !parse_toplevel( ws->local_kit, 0 ) ) return( FALSE ); return( TRUE ); } gboolean workspace_local_set_from_file( Workspace *ws, const char *fname ) { iOpenFile *of; char *txt; if( !(of = ifile_open_read( "%s", fname )) ) return( FALSE ); if( !(txt = ifile_read( of )) ) { ifile_close( of ); return( FALSE ); } if( !workspace_local_set( ws, txt ) ) { g_free( txt ); ifile_close( of ); return( FALSE ); } filemodel_set_filename( FILEMODEL( ws->local_kit ), fname ); g_free( txt ); ifile_close( of ); return( TRUE ); } static gint workspace_jump_name_compare( iContainer *a, iContainer *b ) { int la = strlen( IOBJECT( a )->name ); int lb = strlen( IOBJECT( b )->name ); /* Smaller names first. */ if( la == lb ) return( strcmp( IOBJECT( a )->name, IOBJECT( b )->name ) ); else return( la - lb ); } static void workspace_jump_column_cb( GtkWidget *item, Column *column ) { column_scrollto( column, MODEL_SCROLL_TOP ); } static void * workspace_jump_build( Column *column, GtkWidget *menu ) { GtkWidget *item; char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_appendf( &buf, "%s - %s", IOBJECT( column )->name, IOBJECT( column )->caption ); item = gtk_menu_item_new_with_label( vips_buf_all( &buf ) ); g_signal_connect( item, "activate", G_CALLBACK( workspace_jump_column_cb ), column ); gtk_menu_append( GTK_MENU( menu ), item ); gtk_widget_show( item ); return( NULL ); } /* Update a menu with the set of current columns. */ void workspace_jump_update( Workspace *ws, GtkWidget *menu ) { GSList *columns; gtk_container_foreach( GTK_CONTAINER( menu ), (GtkCallback) gtk_widget_destroy, NULL ); columns = icontainer_get_children( ICONTAINER( ws ) ); columns = g_slist_sort( columns, (GCompareFunc) workspace_jump_name_compare ); slist_map( columns, (SListMapFn) workspace_jump_build, menu ); g_slist_free( columns ); } /* Merge file into this workspace. */ gboolean workspace_merge_file( Workspace *ws, const char *filename ) { Workspacegroup *wsg = workspace_get_workspacegroup( ws ); icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); return( workspacegroup_merge_columns( wsg, filename ) ); } /* Duplicate selected rows in this workspace. */ gboolean workspace_selected_duplicate( Workspace *ws ) { Workspacegroup *wsg = workspace_get_workspacegroup( ws ); char filename[FILENAME_MAX]; if( !workspace_selected_any( ws ) ) { Row *row; if( (row = workspace_get_bottom( ws )) ) row_select( row ); } if( !temp_name( filename, "ws" ) ) return( FALSE ); if( !workspace_selected_save( ws, filename ) ) return( FALSE ); progress_begin(); if( !workspacegroup_merge_rows( wsg, filename ) ) { progress_end(); unlinkf( "%s", filename ); return( FALSE ); } unlinkf( "%s", filename ); symbol_recalculate_all(); workspace_deselect_all( ws ); column_scrollto( workspace_get_column( ws ), MODEL_SCROLL_BOTTOM ); progress_end(); return( TRUE ); } /* Bounding box of columns to be saved. Though we only really set top/left. */ static void * workspace_selected_save_box( Column *col, Rect *box ) { if( model_save_test( MODEL( col ) ) ) { if( im_rect_isempty( box ) ) { box->left = col->x; box->top = col->y; box->width = 100; box->height = 100; } else { box->left = IM_MIN( box->left, col->x ); box->top = IM_MIN( box->top, col->y ); } } return( NULL ); } /* Save just the selected objects. */ gboolean workspace_selected_save( Workspace *ws, const char *filename ) { Workspacegroup *wsg = workspace_get_workspacegroup( ws ); Rect box = { 0 }; icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); workspace_map_column( ws, (column_map_fn) workspace_selected_save_box, &box ); filemodel_set_offset( FILEMODEL( wsg ), box.left, box.top ); if( !workspacegroup_save_selected( wsg, filename ) ) return( FALSE ); return( TRUE ); } gboolean workspace_rename( Workspace *ws, const char *name, const char *caption ) { if( !symbol_rename( ws->sym, name ) ) return( FALSE ); iobject_set( IOBJECT( ws ), IOBJECT( ws->sym )->name, caption ); workspace_set_modified( ws, TRUE ); symbol_recalculate_all(); return( TRUE ); } void workspace_set_locked( Workspace *ws, gboolean locked ) { if( ws->locked != locked ) { ws->locked = locked; iobject_changed( IOBJECT( ws ) ); workspace_set_modified( ws, TRUE ); } } gboolean workspace_duplicate( Workspace *ws ) { Workspacegroup *wsg = workspace_get_workspacegroup( ws ); char filename[FILENAME_MAX]; if( !temp_name( filename, "ws" ) ) return( FALSE ); icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); if( !workspacegroup_save_current( wsg, filename ) ) return( FALSE ); progress_begin(); if( !workspacegroup_merge_workspaces( wsg, filename ) ) { progress_end(); unlinkf( "%s", filename ); return( FALSE ); } unlinkf( "%s", filename ); symbol_recalculate_all(); progress_end(); return( TRUE ); } ================================================ FILE: src/workspace.h ================================================ /* Declarations for workspace.c. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_WORKSPACE (workspace_get_type()) #define WORKSPACE( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WORKSPACE, Workspace )) #define WORKSPACE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WORKSPACE, WorkspaceClass)) #define IS_WORKSPACE( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WORKSPACE )) #define IS_WORKSPACE_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WORKSPACE )) #define WORKSPACE_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WORKSPACE, WorkspaceClass )) /* Three sorts of workspace file load. */ typedef enum { WORKSPACE_MODE_REGULAR, /* Vanilla! */ WORKSPACE_MODE_FORMULA, /* Show formula, not values */ WORKSPACE_MODE_NOEDIT /* Shut down all edits */ } WorkspaceMode; /* A workspace. */ struct _Workspace { Model parent_object; /* Our rows are part of this symbol. */ Symbol *sym; /* We are using this set of toolkits. */ Toolkitgroup *kitg; /* State. */ int next; /* Index for next column name */ GSList *selected; /* Rows selected in this workspace */ GSList *errors; /* Rows with errors */ WorkspaceMode mode; /* Display mode */ gboolean locked; /* WS has been locked */ /* The nip2 version that made this workspace. */ int major; int minor; /* We may load some compat definitions to support this workspace, if it * was written by an older version. * * The version number of the compat stuff we loaded. Zero for no compat * stuff loaded. */ int compat_major; int compat_minor; /* The last row we scrolled to on next-error. */ Row *last_error; Rect area; /* Rect enclosing the set of columns */ Rect vp; /* Viewport hint ... set by views */ gboolean lpane_open; /* Pane model */ int lpane_position; gboolean rpane_open; int rpane_position; char *status; /* Status message */ /* Visualisation defaults for this ws. */ double scale; double offset; /* Workspace-local defs, displayed in the left pane. All in a private * kitg and kit. */ char *local_defs; Toolkitgroup *local_kitg; Toolkit *local_kit; /* Some view inside this ws has changed and this ws needs a relayout. * Use in_dispose to stop relayout during ws shutdown. */ gboolean needs_layout; gboolean in_dispose; }; typedef struct _WorkspaceClass { ModelClass parent_class; /* Methods. */ } WorkspaceClass; void workspace_set_needs_layout( Workspace *ws, gboolean needs_layout ); GSList *workspace_get_needs_layout(); Workspacegroup *workspace_get_workspacegroup( Workspace *ws ); Workspaceroot *workspace_get_workspaceroot( Workspace *ws ); void workspace_set_modified( Workspace *ws, gboolean modified ); void *workspace_map( workspace_map_fn fn, void *a, void *b ); void *workspace_map_column( Workspace *ws, column_map_fn fn, void *a ); void *workspace_map_symbol( Workspace *ws, symbol_map_fn fn, void *a ); void *workspace_map_view( Workspace *ws, view_map_fn fn, void *a ); gboolean workspace_is_empty( Workspace *ws ); void *workspace_selected_map( Workspace *ws, row_map_fn fn, void *a, void *b ); void *workspace_selected_map_sym( Workspace *ws, symbol_map_fn fn, void *a, void *b ); gboolean workspace_selected_any( Workspace *ws ); int workspace_selected_num( Workspace *ws ); gboolean workspace_selected_sym( Workspace *ws, Symbol *sym ); Row *workspace_selected_one( Workspace *ws ); void workspace_deselect_all( Workspace *ws ); void workspace_selected_names( Workspace *ws, VipsBuf *buf, const char *separator ); void workspace_column_names( Column *col, VipsBuf *buf, const char *separator ); void workspace_select_all( Workspace *ws ); Column *workspace_is_one_empty( Workspace *ws ); Column *workspace_column_find( Workspace *ws, const char *name ); Column *workspace_column_get( Workspace *ws, const char *name ); void workspace_column_name_new( Workspace *ws, char *name ); Column *workspace_column_pick( Workspace *ws ); void workspace_column_select( Workspace *ws, Column *col ); Column *workspace_column_new( Workspace *ws ); Symbol *workspace_add_def( Workspace *ws, const char *str ); Symbol *workspace_add_def_recalc( Workspace *ws, const char *str ); gboolean workspace_load_file_buf( VipsBuf *buf, const char *filename ); Symbol *workspace_load_file( Workspace *ws, const char *filename ); void workspace_get_version( Workspace *ws, int *major, int *minor ); int workspace_have_compat( int major, int minor, int *best_major, int *best_minor ); gboolean workspace_load_compat( Workspace *ws, int major, int minor ); GType workspace_get_type( void ); Workspace *workspace_new( Workspacegroup *wsg, const char *name ); Workspace *workspace_new_blank( Workspacegroup *wsg ); gboolean workspace_add_action( Workspace *ws, const char *name, const char *action, int nparam ); int workspace_number( void ); gboolean workspace_selected_recalc( Workspace *ws ); void workspace_selected_remove_yesno( Workspace *ws, GtkWidget *parent ); gboolean workspace_selected_ungroup( Workspace *ws ); gboolean workspace_selected_group( Workspace *ws ); gboolean workspace_next_error( Workspace *ws ); void workspace_set_status( Workspace *ws, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void workspace_set_mode( Workspace *ws, WorkspaceMode mode ); gboolean workspace_local_set( Workspace *ws, const char *txt ); gboolean workspace_local_set_from_file( Workspace *ws, const char *fname ); void workspace_jump_update( Workspace *ws, GtkWidget *menu ); gboolean workspace_merge_file( Workspace *ws, const char *filename ); gboolean workspace_selected_duplicate( Workspace *ws ); gboolean workspace_selected_save( Workspace *ws, const char *filename ); gboolean workspace_rename( Workspace *ws, const char *name, const char *caption ); void workspace_set_locked( Workspace *ws, gboolean locked ); gboolean workspace_duplicate( Workspace *ws ); ================================================ FILE: src/workspacedefs.c ================================================ /* Workspace-local defs. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ViewClass *parent_class = NULL; static void workspacedefs_text_changed( GtkTextBuffer *buffer, Workspacedefs *workspacedefs ) { #ifdef DEBUG printf( "workspacedefs_text_changed\n" ); #endif /*DEBUG*/ if( !workspacedefs->changed ) { workspacedefs->changed = TRUE; #ifdef DEBUG printf( "\t(changed = TRUE)\n" ); #endif /*DEBUG*/ /* The workspace hasn't changed, but this will queue a refresh * on us. */ iobject_changed( IOBJECT( workspacedefs->ws ) ); } } static void workspacedefs_refresh( vObject *vobject ) { Workspacedefs *workspacedefs = WORKSPACEDEFS( vobject ); Workspace *ws = workspacedefs->ws; char txt[256]; VipsBuf buf = VIPS_BUF_STATIC( txt ); #ifdef DEBUG printf( "workspacedefs_refresh:\n" ); #endif /*DEBUG*/ if( !workspacedefs->changed ) { guint text_hash = g_str_hash( ws->local_defs ); if( text_hash != workspacedefs->text_hash ) { g_signal_handlers_block_by_func( gtk_text_view_get_buffer( GTK_TEXT_VIEW( workspacedefs->text ) ), workspacedefs_text_changed, workspacedefs ); text_view_set_text( GTK_TEXT_VIEW( workspacedefs->text ), ws->local_defs, TRUE ); g_signal_handlers_unblock_by_func( gtk_text_view_get_buffer( GTK_TEXT_VIEW( workspacedefs->text ) ), workspacedefs_text_changed, workspacedefs ); workspacedefs->text_hash = text_hash; } } if( ws->local_kit ) { int n = icontainer_get_n_children( ICONTAINER( ws->local_kit ) ); vips_buf_appendf( &buf, ngettext( "%d definition", "%d definitions", n ), n ); } if( workspacedefs->errors ) { if( !vips_buf_is_empty( &buf ) ) vips_buf_appendf( &buf, ", " ); vips_buf_appendf( &buf, _( "errors" ) ); } if( workspacedefs->changed ) { if( !vips_buf_is_empty( &buf ) ) vips_buf_appendf( &buf, ", " ); vips_buf_appendf( &buf, _( "modified" ) ); } set_glabel( workspacedefs->status, "%s", vips_buf_all( &buf ) ); VOBJECT_CLASS( parent_class )->refresh( vobject ); } static void workspacedefs_link( vObject *vobject, iObject *iobject ) { Workspacedefs *workspacedefs = WORKSPACEDEFS( vobject ); Workspace *ws = WORKSPACE( iobject ); g_assert( !workspacedefs->ws ); workspacedefs->ws = ws; VOBJECT_CLASS( parent_class )->link( vobject, iobject ); } static void workspacedefs_class_init( WorkspacedefsClass *class ) { vObjectClass *vobject_class = (vObjectClass *) class; parent_class = g_type_class_peek_parent( class ); vobject_class->refresh = workspacedefs_refresh; vobject_class->link = workspacedefs_link; } static gboolean workspacedefs_set_text_from_file( Workspacedefs *workspacedefs, const char *fname ) { Workspace *ws = workspacedefs->ws; workspacedefs->changed = FALSE; workspacedefs->errors = FALSE; if( !workspace_local_set_from_file( ws, fname ) ) { text_view_select_text( GTK_TEXT_VIEW( workspacedefs->text ), input_state.charpos - yyleng, input_state.charpos ); workspacedefs->errors = TRUE; return( FALSE ); } symbol_recalculate_all(); return( TRUE ); } /* Callback from load browser. */ static void workspacedefs_load_file_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Workspacedefs *workspacedefs = WORKSPACEDEFS( client ); char *fname; if( !(fname = filesel_get_filename( filesel )) ) { nfn( sys, IWINDOW_ERROR ); return; } if( !workspacedefs_set_text_from_file( workspacedefs, fname ) ) { g_free( fname ); nfn( sys, IWINDOW_ERROR ); return; } g_free( fname ); nfn( sys, IWINDOW_YES ); } static void workspacedefs_replace_cb( GtkWidget *wid, Workspacedefs *workspacedefs ) { GtkWidget *filesel; filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Replace Definition From File" ) ); filesel_set_flags( FILESEL( filesel ), FALSE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_definition, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( wid ) ); filesel_set_done( FILESEL( filesel ), workspacedefs_load_file_cb, workspacedefs ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } static void workspacedefs_save_as_cb( GtkWidget *wid, Workspacedefs *workspacedefs ) { Workspace *ws = workspacedefs->ws; if( ws->local_kit ) filemodel_inter_saveas( IWINDOW( wid ), FILEMODEL( ws->local_kit ) ); } static gboolean workspacedefs_set_text( Workspacedefs *workspacedefs, const char *txt ) { Workspace *ws = workspacedefs->ws; workspacedefs->changed = FALSE; workspacedefs->errors = FALSE; workspacedefs->text_hash = g_str_hash( txt ); if( !workspace_local_set( ws, txt ) ) { text_view_select_text( GTK_TEXT_VIEW( workspacedefs->text ), input_state.charpos - yyleng, input_state.charpos ); workspacedefs->errors = TRUE; return( FALSE ); } symbol_recalculate_all(); return( TRUE ); } /* "Process" in defs area. */ static void workspacedefs_process_cb( GtkWidget *wid, Workspacedefs *workspacedefs ) { char *txt; #ifdef DEBUG printf( "workspacedefs_process_cb:\n" ); printf( "\tchanged = FALSE\n" ); #endif /*DEBUG*/ txt = text_view_get_text( GTK_TEXT_VIEW( workspacedefs->text ) ); if( !workspacedefs_set_text( workspacedefs, txt ) ) iwindow_alert( wid, GTK_MESSAGE_ERROR ); g_free( txt ); } static void workspacedefs_init( Workspacedefs *workspacedefs ) { GtkWidget *pane; Popupbutton *popupbutton; GtkWidget *swin; GtkWidget *hbox; GtkWidget *but; #ifdef DEBUG printf( "workspacedefs_init:\n" ); #endif /*DEBUG*/ workspacedefs->changed = FALSE; workspacedefs->errors = FALSE; workspacedefs->text_hash = 0; pane = menu_build( _( "Workspace definitions" ) ); menu_add_but( pane, _( "Replace From _File" ), GTK_SIGNAL_FUNC( workspacedefs_replace_cb ), workspacedefs ); menu_add_but( pane, GTK_STOCK_SAVE_AS, GTK_SIGNAL_FUNC( workspacedefs_save_as_cb ), workspacedefs ); hbox = gtk_hbox_new( FALSE, 7 ); gtk_box_pack_start( GTK_BOX( workspacedefs ), hbox, FALSE, FALSE, 0 ); gtk_widget_show( hbox ); popupbutton = popupbutton_new(); popupbutton_set_menu( popupbutton, pane ); gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( popupbutton ), FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( popupbutton ) ); but = gtk_button_new_with_label( _( "Process" ) ); g_signal_connect( G_OBJECT( but ), "clicked", G_CALLBACK( workspacedefs_process_cb ), workspacedefs ); gtk_box_pack_start( GTK_BOX( hbox ), but, FALSE, FALSE, 0 ); gtk_widget_show( but ); workspacedefs->status = gtk_label_new( NULL ); gtk_misc_set_alignment( GTK_MISC( workspacedefs->status ), 0, 0.5 ); gtk_box_pack_start( GTK_BOX( hbox ), workspacedefs->status, TRUE, TRUE, 0 ); gtk_widget_show( workspacedefs->status ); swin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( swin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_box_pack_end( GTK_BOX( workspacedefs ), swin, TRUE, TRUE, 0 ); gtk_widget_show( swin ); workspacedefs->text = program_text_new(); g_signal_connect( gtk_text_view_get_buffer( GTK_TEXT_VIEW( workspacedefs->text ) ), "changed", G_CALLBACK( workspacedefs_text_changed ), workspacedefs ); gtk_container_add( GTK_CONTAINER( swin ), workspacedefs->text ); gtk_widget_show( workspacedefs->text ); } GtkType workspacedefs_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Workspacedefs", sizeof( Workspacedefs ), sizeof( WorkspacedefsClass ), (GtkClassInitFunc) workspacedefs_class_init, (GtkObjectInitFunc) workspacedefs_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VOBJECT, &info ); } return( type ); } Workspacedefs * workspacedefs_new( void ) { Workspacedefs *workspacedefs = gtk_type_new( TYPE_WORKSPACEDEFS ); return( workspacedefs ); } ================================================ FILE: src/workspacedefs.h ================================================ /* Workspace-local defs */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_WORKSPACEDEFS (workspacedefs_get_type()) #define WORKSPACEDEFS( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_WORKSPACEDEFS, Workspacedefs )) #define WORKSPACEDEFS_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_WORKSPACEDEFS, WorkspacedefsClass )) #define IS_WORKSPACEDEFS( obj ) \ (GTK_CHECK_TYPE( (obj), TYPE_WORKSPACEDEFS )) #define IS_WORKSPACEDEFS_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_WORKSPACEDEFS )) struct _Workspacedefs { vObject parent_object; Workspace *ws; /* Workspace we explore */ GtkWidget *text; gboolean changed; /* Text has been edited */ gboolean errors; /* Error on last process */ guint text_hash; /* Hash of the last text we set */ GtkWidget *status; }; typedef struct _WorkspacedefsClass { vObjectClass parent_class; } WorkspacedefsClass; GtkType workspacedefs_get_type( void ); Workspacedefs *workspacedefs_new( void ); ================================================ FILE: src/workspacegroup.c ================================================ /* A set of workspaces loaded and saved from a ws file. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static FilemodelClass *parent_class = NULL; void workspacegroup_set_load_type( Workspacegroup *wsg, WorkspacegroupLoadType load_type ) { wsg->load_type = load_type; } void workspacegroup_set_save_type( Workspacegroup *wsg, WorkspacegroupSaveType save_type ) { wsg->save_type = save_type; } Workspace * workspacegroup_get_workspace( Workspacegroup *wsg ) { if( ICONTAINER( wsg )->current ) return( WORKSPACE( ICONTAINER( wsg )->current ) ); if( ICONTAINER( wsg )->children ) return( WORKSPACE( ICONTAINER( wsg )->children->data ) ); return( NULL ); } static Workspace * workspacegroup_workspace_pick( Workspacegroup *wsg ) { Workspace *ws; if( (ws = workspacegroup_get_workspace( wsg )) ) return( ws ); if( ICONTAINER( wsg )->children ) { ws = WORKSPACE( ICONTAINER( wsg )->children->data ); icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); return( ws ); } ws = workspace_new_blank( wsg ); (void) workspace_column_pick( ws ); return( ws ); } Workspace * workspacegroup_map( Workspacegroup *wsg, workspace_map_fn fn, void *a, void *b ) { return( (Workspace *) icontainer_map( ICONTAINER( wsg ), (icontainer_map_fn) fn, a, b ) ); } static void * workspacegroup_is_empty_sub( Workspace *ws, gboolean *empty ) { if( !workspace_is_empty( ws ) ) { *empty = FALSE; return( ws ); } return( NULL ); } gboolean workspacegroup_is_empty( Workspacegroup *wsg ) { gboolean empty; empty = TRUE; (void) workspacegroup_map( wsg, (workspace_map_fn) workspacegroup_is_empty_sub, &empty, NULL ); return( empty ); } static void * workspacegroup_get_n_objects_sub( Workspace *ws, int *n_objects ) { Compile *compile = ws->sym->expr->compile; *n_objects += g_slist_length( ICONTAINER( compile )->children ); return( NULL ); } int workspacegroup_get_n_objects( Workspacegroup *wsg ) { int n_objects; n_objects = 0; workspacegroup_map( wsg, (workspace_map_fn) workspacegroup_get_n_objects_sub, &n_objects, NULL ); return( n_objects ); } static void workspacegroup_dispose( GObject *gobject ) { Workspacegroup *wsg; g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WORKSPACEGROUP( gobject ) ); wsg = WORKSPACEGROUP( gobject ); #ifdef DEBUG printf( "workspacegroup_dispose %s\n", IOBJECT( wsg )->name ); #endif /*DEBUG*/ IM_FREEF( g_source_remove, wsg->autosave_timeout ); G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static View * workspacegroup_view_new( Model *model, View *parent ) { return( workspacegroupview_new() ); } static void * workspacegroup_save_sub( iContainer *icontainer, void *a, void *b ) { Workspace *ws = WORKSPACE( icontainer ); xmlNode *xnode = (xmlNode *) a; Workspacegroup *wsg = WORKSPACEGROUP( b ); /* Only save all workspaces in save-all mode. */ if( wsg->save_type != WORKSPACEGROUP_SAVE_ALL && WORKSPACE( ICONTAINER( wsg )->current ) != ws ) return( NULL ); return( model_save( MODEL( ws ), xnode ) ); } static xmlNode * workspacegroup_save( Model *model, xmlNode *xnode ) { /* We normally chain up like this: * * xthis = MODEL_CLASS( parent_class )->save( model, xnode ) * * but that will make a workspacegroup holding our workspaces. Instead * we want to save all our workspaces directly to xnode with nothing * about us in there. * * See model_real_save(). */ if( icontainer_map( ICONTAINER( model ), workspacegroup_save_sub, xnode, model ) ) return( NULL ); return( xnode ); } /* Loops over xml trees follow this pattern. */ #define FOR_ALL_XML( ROOT, CHILD, CHILD_NAME ) { \ xmlNode *CHILD; \ \ for( CHILD = ROOT->children; CHILD; CHILD = CHILD->next ) { \ if( strcmp( (char *) CHILD->name, CHILD_NAME ) != 0 ) \ continue; #define FOR_ALL_XML_END } } static void workspacegroup_rename_workspace_node( Workspacegroup *wsg, ModelLoadState *state, xmlNode *xws ) { Workspaceroot *wsr = wsg->wsr; char name[MAX_STRSIZE]; char new_name[MAX_STRSIZE]; if( !get_sprop( xws, "name", name, MAX_STRSIZE ) ) return; strcpy( new_name, name ); while( compile_lookup( wsr->sym->expr->compile, new_name ) || model_loadstate_taken( state, new_name ) ) increment_name( new_name ); (void) set_sprop( xws, "name", new_name ); (void) model_loadstate_rename_new( state, name, new_name ); } /* Does a scrap of XML need compat defs? */ static gboolean workspacegroup_xml_needs_compat( ModelLoadState *state, xmlNode *xws, int *best_major, int *best_minor ) { int major; int minor; /* What version is the XML expecting? A combination of the version of * nip that saved the file, and any compat notes on the workspace XML */ if( !get_iprop( xws, "major", &major ) || !get_iprop( xws, "minor", &minor ) ) { /* Fall back to the version number in the xml header. */ major = state->major; minor = state->minor; } /* Find the best set of compat we have. */ return( workspace_have_compat( major, minor, best_major, best_minor ) ); } /* Load all workspaces into this wsg. */ static gboolean workspacegroup_load_new( Workspacegroup *wsg, ModelLoadState *state, xmlNode *xroot ) { Workspace *first_ws; /* Rename ... new names for any workspaces which clash. */ FOR_ALL_XML( xroot, xws, "Workspace" ) { workspacegroup_rename_workspace_node( wsg, state, xws ); } FOR_ALL_XML_END /* _front() the first ws we load. Needed for things like duplicate ws * and merge wses. */ first_ws = NULL; FOR_ALL_XML( xroot, xws, "Workspace" ) { char name[MAX_STRSIZE]; Workspace *ws; int major; int minor; column_set_offset( WORKSPACEVIEW_MARGIN_LEFT, WORKSPACEVIEW_MARGIN_TOP ); if( !get_sprop( xws, "name", name, FILENAME_MAX ) || !(ws = workspace_new( wsg, name )) ) return( FALSE ); if( workspacegroup_xml_needs_compat( state, xws, &major, &minor ) && !workspace_load_compat( ws, major, minor ) ) return( FALSE ); if( model_load( MODEL( ws ), state, MODEL( wsg ), xws ) ) return( FALSE ); if( !first_ws ) first_ws = ws; } FOR_ALL_XML_END if( first_ws ) icontainer_current( ICONTAINER( wsg ), ICONTAINER( first_ws ) ); return( TRUE ); } static void workspacegroup_rename_row_node( Workspace *ws, ModelLoadState *state, const char *col_name, xmlNode *xrow ) { char old_name[MAX_STRSIZE]; char new_name[MAX_STRSIZE]; if( !get_sprop( xrow, "name", old_name, MAX_STRSIZE ) ) return; im_snprintf( new_name, MAX_STRSIZE, "%s1", col_name ); while( compile_lookup( ws->sym->expr->compile, new_name ) || model_loadstate_taken( state, new_name ) ) increment_name( new_name ); (void) set_sprop( xrow, "name", new_name ); (void) model_loadstate_rename_new( state, old_name, new_name ); #ifdef DEBUG printf( "workspacegroup_rename_row_node: renaming " "'%s' to '%s'\n", old_name, new_name ); #endif } /* Rename column if there's one of that name in workspace. */ static void workspacegroup_rename_column_node( Workspacegroup *wsg, Workspace *ws, ModelLoadState *state, xmlNode *xcol ) { char name[MAX_STRSIZE]; char new_name[256]; if( !get_sprop( xcol, "name", name, MAX_STRSIZE ) ) return; im_strncpy( new_name, name, 256 ); while( workspace_column_find( ws, new_name ) || model_loadstate_column_taken( state, new_name ) ) workspace_column_name_new( ws, new_name ); if( strcmp( name, new_name ) != 0 ) { #ifdef DEBUG printf( "workspace_rename_column_node: renaming column " "%s to %s\n", name, new_name ); #endif /*DEBUG*/ (void) set_sprop( xcol, "name", new_name ); (void) model_loadstate_column_rename_new( state, name, new_name ); /* And allocate new names for all rows in the subcolumn. */ FOR_ALL_XML( xcol, xsub, "Subcolumn" ) { FOR_ALL_XML( xsub, xrow, "Row" ) { workspacegroup_rename_row_node( ws, state, new_name, xrow ); } FOR_ALL_XML_END } FOR_ALL_XML_END } } /* Load at column level ... rename columns which clash with * columns in the current workspace. Also look out for clashes * with columns we will load. */ static gboolean workspacegroup_load_columns( Workspacegroup *wsg, ModelLoadState *state, xmlNode *xroot ) { Workspace *ws = workspacegroup_workspace_pick( wsg ); int xml_major; int xml_minor; gboolean found; int ws_major; int ws_minor; /* Look for any compat problems. */ found = FALSE; FOR_ALL_XML( xroot, xws, "Workspace" ) { if( workspacegroup_xml_needs_compat( state, xws, &xml_major, &xml_minor ) ) { found = TRUE; break; } } FOR_ALL_XML_END workspace_get_version( ws, &ws_major, &ws_minor ); if( found && (xml_major != ws_major || xml_minor != ws_minor) ) { error_top( _( "Version mismatch." ) ); error_sub( _( "File \"%s\" needs version %d.%d. Merging " "into this tab may cause compatibility problems." ), state->filename, xml_major, xml_minor ); iwindow_alert( GTK_WIDGET( wsg->iwnd ), GTK_MESSAGE_INFO ); } /* Search all the columns we will load for their names and add rename * rules. */ FOR_ALL_XML( xroot, xws, "Workspace" ) { FOR_ALL_XML( xws, xcol, "Column" ) { workspacegroup_rename_column_node( wsg, ws, state, xcol ); } FOR_ALL_XML_END } FOR_ALL_XML_END /* Load those columns. */ FOR_ALL_XML( xroot, xws, "Workspace" ) { FOR_ALL_XML( xws, xcol, "Column" ) { if( !model_new_xml( state, MODEL( ws ), xcol ) ) return( FALSE ); } FOR_ALL_XML_END } FOR_ALL_XML_END return( TRUE ); } /* Load at row level ... merge into the current column. */ static gboolean workspacegroup_load_rows( Workspacegroup *wsg, ModelLoadState *state, xmlNode *xroot ) { Workspace *ws = workspacegroup_workspace_pick( wsg ); Column *col = workspace_column_pick( ws ); int xml_major; int xml_minor; gboolean found; int ws_major; int ws_minor; /* Look for any compat problems. */ found = FALSE; FOR_ALL_XML( xroot, xws, "Workspace" ) { if( workspacegroup_xml_needs_compat( state, xws, &xml_major, &xml_minor ) ) { found = TRUE; break; } } FOR_ALL_XML_END workspace_get_version( ws, &ws_major, &ws_minor ); if( found && (xml_major != ws_major || xml_minor != ws_minor) ) { error_top( _( "Version mismatch." ) ); error_sub( _( "File \"%s\" needs version %d.%d. Merging " "into this tab may cause compatibility problems." ), state->filename, xml_major, xml_minor ); iwindow_alert( GTK_WIDGET( wsg->iwnd ), GTK_MESSAGE_INFO ); } FOR_ALL_XML( xroot, xws, "Workspace" ) { FOR_ALL_XML( xws, xcol, "Column" ) { FOR_ALL_XML( xcol, xsub, "Subcolumn" ) { FOR_ALL_XML( xsub, xrow, "Row" ) { workspacegroup_rename_row_node( ws, state, IOBJECT( col )->name, xrow ); } FOR_ALL_XML_END } FOR_ALL_XML_END } FOR_ALL_XML_END } FOR_ALL_XML_END FOR_ALL_XML( xroot, xws, "Workspace" ) { FOR_ALL_XML( xws, xcol, "Column" ) { FOR_ALL_XML( xcol, xsub, "Subcolumn" ) { FOR_ALL_XML( xsub, xrow, "Row" ) { if( !model_new_xml( state, MODEL( col->scol ), xrow ) ) return( FALSE ); } FOR_ALL_XML_END } FOR_ALL_XML_END } FOR_ALL_XML_END } FOR_ALL_XML_END return( TRUE ); } static gboolean workspacegroup_top_load( Filemodel *filemodel, ModelLoadState *state, Model *parent, xmlNode *xroot ) { Workspacegroup *wsg = WORKSPACEGROUP( filemodel ); xmlNode *xnode; char name[FILENAME_MAX]; #ifdef DEBUG printf( "workspacegroup_top_load: from %s\n", state->filename ); #endif /*DEBUG*/ /* The top node should be the first workspace. Get the filename this * workspace was saved as so we can work out how to rewrite embedded * filenames. * * The filename field can be missing. */ if( (xnode = get_node( xroot, "Workspace" )) && get_sprop( xnode, "filename", name, FILENAME_MAX ) ) { char *new_dir; /* The old filename could be non-native, so we must rewrite * to native form first so g_path_get_dirname() can work. */ path_compact( name ); state->old_dir = g_path_get_dirname( name ); new_dir = g_path_get_dirname( state->filename_user ); path_rewrite_add( state->old_dir, new_dir, FALSE ); g_free( new_dir ); } switch( wsg->load_type ) { case WORKSPACEGROUP_LOAD_NEW: if( !workspacegroup_load_new( wsg, state, xroot ) ) return( FALSE ); break; case WORKSPACEGROUP_LOAD_COLUMNS: if( !workspacegroup_load_columns( wsg, state, xroot ) ) return( FALSE ); break; case WORKSPACEGROUP_LOAD_ROWS: if( !workspacegroup_load_rows( wsg, state, xroot ) ) return( FALSE ); break; default: g_assert( FALSE ); } return( FILEMODEL_CLASS( parent_class )->top_load( filemodel, state, parent, xnode ) ); } static gboolean workspacegroup_top_save( Filemodel *filemodel, const char *filename ) { gboolean result; #ifdef DEBUG printf( "workspacegroup_top_save: %s to %s\n", NN( IOBJECT( filemodel )->name ), filename ); #endif /*DEBUG*/ if( (result = FILEMODEL_CLASS( parent_class )-> top_save( filemodel, filename )) ) /* This will add save-as files to recent too. Don't note * auto_load on recent, since it won't have been loaded by the * user. */ if( !filemodel->auto_load ) mainw_recent_add( &mainw_recent_workspace, filename ); return( result ); } /* Backup the last WS_RETAIN workspaces. */ #define WS_RETAIN (10) /* Array of names of workspace files we are keeping. */ static char *retain_files[WS_RETAIN] = { NULL }; /* On safe exit, remove all ws checkmarks. */ void workspacegroup_autosave_clean( void ) { int i; for( i = 0; i < WS_RETAIN; i++ ) { if( retain_files[i] ) { unlinkf( "%s", retain_files[i] ); IM_FREE( retain_files[i] ); } } } /* Save the workspace to one of our temp files. */ static gboolean workspacegroup_checkmark_timeout( Workspacegroup *wsg ) { /* The next one we allocate. */ static int retain_next = 0; wsg->autosave_timeout = 0; if( !AUTO_WS_SAVE ) return( FALSE ); /* Don't backup auto loaded workspace (eg. preferences). These are * system things and don't need it. */ if( FILEMODEL( wsg )->auto_load ) return( FALSE ); /* Do we have a name for this retain file? */ if( !retain_files[retain_next] ) { char filename[FILENAME_MAX]; /* No name yet - make one up. */ if( !temp_name( filename, "ws" ) ) return( FALSE ); retain_files[retain_next] = im_strdup( NULL, filename ); } if( !filemodel_top_save( FILEMODEL( wsg ), retain_files[retain_next] ) ) return( FALSE ); retain_next = (retain_next + 1) % WS_RETAIN; return( FALSE ); } /* Save the workspace to one of our temp files. Don't save directly (pretty * slow), instead set a timeout and save when we're quiet for >1s. */ static void workspacegroup_checkmark( Workspacegroup *wsg ) { if( !AUTO_WS_SAVE ) return; if( FILEMODEL( wsg )->auto_load ) return; IM_FREEF( g_source_remove, wsg->autosave_timeout ); wsg->autosave_timeout = g_timeout_add( 1000, (GSourceFunc) workspacegroup_checkmark_timeout, wsg ); } typedef struct { /* Best so far filename. */ char filename[FILENAME_MAX]; /* Best-so-far file date. */ time_t time; } AutoRecover; /* This file any better than the previous best candidate? Subfn of below. */ static void * workspacegroup_test_file( const char *name, void *a, void *b, void *c ) { AutoRecover *recover = (AutoRecover *) a; char buf[FILENAME_MAX]; time_t time; int i; im_strncpy( buf, name, FILENAME_MAX ); path_expand( buf ); for( i = 0; i < WS_RETAIN; i++ ) if( retain_files[i] && strcmp( buf, retain_files[i] ) == 0 ) return( NULL ); if( !(time = mtime( "%s", buf )) ) return( NULL ); if( recover->time > 0 && time < recover->time ) return( NULL ); strcpy( recover->filename, buf ); recover->time = time; return( NULL ); } /* Search for the most recent "*.ws" file * in the tmp area owned by us, with a size > 0, that's not in our * retain_files[] set. */ char * workspacegroup_autosave_recover( void ) { AutoRecover recover; strcpy( recover.filename, "" ); recover.time = 0; (void) path_map_dir( PATH_TMP, "*.ws", (path_map_fn) workspacegroup_test_file, &recover ); if( !recover.time ) return( NULL ); return( g_strdup( recover.filename ) ); } static void workspacegroup_set_modified( Filemodel *filemodel, gboolean modified ) { Workspacegroup *wsg = WORKSPACEGROUP( filemodel ); workspacegroup_checkmark( wsg ); FILEMODEL_CLASS( parent_class )->set_modified( filemodel, modified ); } static void workspacegroup_class_init( WorkspacegroupClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iObjectClass *iobject_class = (iObjectClass *) class; ModelClass *model_class = (ModelClass *) class; FilemodelClass *filemodel_class = (FilemodelClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = workspacegroup_dispose; iobject_class->user_name = _( "Workspace" ); /* ->load() is done by workspace_top_load(). */ model_class->view_new = workspacegroup_view_new; model_class->save = workspacegroup_save; filemodel_class->filetype = filesel_type_workspace; filemodel_class->top_load = workspacegroup_top_load; filemodel_class->top_save = workspacegroup_top_save; filemodel_class->set_modified = workspacegroup_set_modified; } static void workspacegroup_init( Workspacegroup *wsg ) { } GType workspacegroup_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( WorkspacegroupClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) workspacegroup_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Workspacegroup ), 32, /* n_preallocs */ (GInstanceInitFunc) workspacegroup_init, }; type = g_type_register_static( TYPE_FILEMODEL, "Workspacegroup", &info, 0 ); } return( type ); } static void workspacegroup_link( Workspacegroup *wsg, Workspaceroot *wsr ) { icontainer_child_add( ICONTAINER( wsr ), ICONTAINER( wsg ), -1 ); wsg->wsr = wsr; filemodel_register( FILEMODEL( wsg ) ); } Workspacegroup * workspacegroup_new( Workspaceroot *wsr ) { Workspacegroup *wsg; #ifdef DEBUG printf( "workspacegroup_new:\n" ); #endif /*DEBUG*/ wsg = WORKSPACEGROUP( g_object_new( TYPE_WORKSPACEGROUP, NULL ) ); /* Changed later. */ iobject_set( IOBJECT( wsg ), "untitled", _( "Empty workspace" ) ); workspacegroup_link( wsg, wsr ); filemodel_set_modified( FILEMODEL( wsg ), FALSE ); return( wsg ); } /* Make the blank workspacegroup we present the user with (in the absence of * anything else). */ Workspacegroup * workspacegroup_new_blank( Workspaceroot *wsr, const char *name ) { Workspacegroup *wsg; if( !(wsg = workspacegroup_new( wsr )) ) return( NULL ); iobject_set( IOBJECT( wsg ), name, NULL ); (void) workspacegroup_workspace_pick( wsg ); filemodel_set_modified( FILEMODEL( wsg ), FALSE ); return( wsg ); } Workspacegroup * workspacegroup_new_filename( Workspaceroot *wsr, const char *filename ) { Workspacegroup *wsg; char name[FILENAME_MAX]; if( !(wsg = workspacegroup_new( wsr )) ) return( NULL ); name_from_filename( filename, name ); iobject_set( IOBJECT( wsg ), name, _( "Default empty workspace" ) ); filemodel_set_filename( FILEMODEL( wsg ), filename ); filemodel_set_modified( FILEMODEL( wsg ), FALSE ); return( wsg ); } /* Load a file as a workspacegroup. */ Workspacegroup * workspacegroup_new_from_file( Workspaceroot *wsr, const char *filename, const char *filename_user ) { Workspacegroup *wsg; if( !(wsg = workspacegroup_new( wsr )) ) return( NULL ); workspacegroup_set_load_type( wsg, WORKSPACEGROUP_LOAD_NEW ); if( !filemodel_load_all( FILEMODEL( wsg ), MODEL( wsr ), filename, filename_user ) ) return( NULL ); filemodel_set_filename( FILEMODEL( wsg ), filename_user ); filemodel_set_modified( FILEMODEL( wsg ), FALSE ); if( filename_user ) { char name[FILENAME_MAX]; name_from_filename( filename_user, name ); iobject_set( IOBJECT( wsg ), name, NULL ); } else iobject_set( IOBJECT( wsg ), "untitled", NULL ); return( wsg ); } /* New workspacegroup from a file. */ Workspacegroup * workspacegroup_new_from_openfile( Workspaceroot *wsr, iOpenFile *of ) { Workspacegroup *wsg; char name[FILENAME_MAX]; #ifdef DEBUG printf( "workspacegroup_new_from_openfile: %s\n", of->fname ); #endif /*DEBUG*/ if( !(wsg = workspacegroup_new( wsr )) ) return( NULL ); workspacegroup_set_load_type( wsg, WORKSPACEGROUP_LOAD_NEW ); if( !filemodel_load_all_openfile( FILEMODEL( wsg ), MODEL( wsr ), of ) ) { g_object_unref( G_OBJECT( wsg ) ); return( NULL ); } filemodel_set_filename( FILEMODEL( wsg ), of->fname ); filemodel_set_modified( FILEMODEL( wsg ), FALSE ); name_from_filename( of->fname, name ); iobject_set( IOBJECT( wsg ), name, NULL ); return( wsg ); } /* Merge into workspacegroup as a set of new workspaces. */ gboolean workspacegroup_merge_workspaces( Workspacegroup *wsg, const char *filename ) { workspacegroup_set_load_type( wsg, WORKSPACEGROUP_LOAD_NEW ); if( !filemodel_load_all( FILEMODEL( wsg ), MODEL( wsg->wsr ), filename, NULL ) ) return( FALSE ); filemodel_set_modified( FILEMODEL( wsg ), TRUE ); return( TRUE ); } /* Merge into the current workspace as a set of columns. */ gboolean workspacegroup_merge_columns( Workspacegroup *wsg, const char *filename ) { Workspace *ws; if( (ws = workspacegroup_get_workspace( wsg )) ) /* We'll do a layout after load, so just load to a huge x and * we'll be OK. */ column_set_offset( 2 * IM_RECT_RIGHT( &ws->area ) + WORKSPACEVIEW_MARGIN_LEFT, WORKSPACEVIEW_MARGIN_TOP ); workspacegroup_set_load_type( wsg, WORKSPACEGROUP_LOAD_COLUMNS ); if( !filemodel_load_all( FILEMODEL( wsg ), MODEL( wsg->wsr ), filename, NULL ) ) return( FALSE ); filemodel_set_modified( FILEMODEL( wsg ), TRUE ); return( TRUE ); } /* Merge into the current workspace as a set of rows. */ gboolean workspacegroup_merge_rows( Workspacegroup *wsg, const char *filename ) { workspacegroup_set_load_type( wsg, WORKSPACEGROUP_LOAD_ROWS ); if( !filemodel_load_all( FILEMODEL( wsg ), MODEL( wsg->wsr ), filename, NULL ) ) return( FALSE ); filemodel_set_modified( FILEMODEL( wsg ), TRUE ); return( TRUE ); } /* Save just the selected objects in the current workspace. */ gboolean workspacegroup_save_selected( Workspacegroup *wsg, const char *filename ) { workspacegroup_set_save_type( wsg, WORKSPACEGROUP_SAVE_SELECTED ); if( !filemodel_top_save( FILEMODEL( wsg ), filename ) ) { unlinkf( "%s", filename ); return( FALSE ); } return( TRUE ); } /* Save just the current workspace. */ gboolean workspacegroup_save_current( Workspacegroup *wsg, const char *filename ) { workspacegroup_set_save_type( wsg, WORKSPACEGROUP_SAVE_WORKSPACE ); if( !filemodel_top_save( FILEMODEL( wsg ), filename ) ) { unlinkf( "%s", filename ); return( FALSE ); } return( TRUE ); } /* Save an entire workspacegroup. */ gboolean workspacegroup_save_all( Workspacegroup *wsg, const char *filename ) { workspacegroup_set_save_type( wsg, WORKSPACEGROUP_SAVE_ALL ); if( !filemodel_top_save( FILEMODEL( wsg ), filename ) ) { unlinkf( "%s", filename ); return( FALSE ); } return( TRUE ); } Workspacegroup * workspacegroup_duplicate( Workspacegroup *wsg ) { Workspaceroot *wsr = wsg->wsr; Workspacegroup *new_wsg; char filename[FILENAME_MAX]; if( !temp_name( filename, "ws" ) || !workspacegroup_save_all( wsg, filename ) ) return( NULL ); if( !(new_wsg = workspacegroup_new_from_file( wsr, filename, FILEMODEL( wsg )->filename )) ) { unlinkf( "%s", filename ); return( NULL ); } unlinkf( "%s", filename ); return( new_wsg ); } ================================================ FILE: src/workspacegroup.h ================================================ /* A set of workspaces loaded and saved from a ws file. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_WORKSPACEGROUP (workspacegroup_get_type()) #define WORKSPACEGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ TYPE_WORKSPACEGROUP, Workspacegroup )) #define WORKSPACEGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ TYPE_WORKSPACEGROUP, WorkspacegroupClass)) #define IS_WORKSPACEGROUP( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WORKSPACEGROUP )) #define IS_WORKSPACEGROUP_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WORKSPACEGROUP )) #define WORKSPACEGROUP_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ TYPE_WORKSPACEGROUP, WorkspacegroupClass )) /* Three sorts of workspace file load. */ typedef enum { WORKSPACEGROUP_LOAD_NEW, /* Load as new workspace */ WORKSPACEGROUP_LOAD_COLUMNS, /* Merge into current workspace */ WORKSPACEGROUP_LOAD_ROWS /* Merge rows into current column */ } WorkspacegroupLoadType; /* Save mode ... controls behaviour of column_save_test() and row_save_test() */ typedef enum { WORKSPACEGROUP_SAVE_ALL, /* Save everything */ WORKSPACEGROUP_SAVE_WORKSPACE, /* Save current workspace */ WORKSPACEGROUP_SAVE_SELECTED /* Only save selected rows */ } WorkspacegroupSaveType; /* Workspacegroups: group workspaces with these. One workspacegroup per * file loaded. */ struct _Workspacegroup { Filemodel parent_class; Workspaceroot *wsr; /* Control load/save for this wsg. */ WorkspacegroupLoadType load_type; WorkspacegroupSaveType save_type; guint autosave_timeout; /* workspacegroup_load_columns() etc. use this to display warnings * during load. */ iWindow *iwnd; }; typedef struct _WorkspacegroupClass { FilemodelClass parent_class; /* My methods. */ } WorkspacegroupClass; Workspace *workspacegroup_get_workspace( Workspacegroup *wsg ); int workspacegroup_get_n_objects( Workspacegroup *wsg ); void workspacegroup_set_load_type( Workspacegroup *wsg, WorkspacegroupLoadType load_type ); void workspacegroup_set_save_type( Workspacegroup *wsg, WorkspacegroupSaveType save_type ); Workspace *workspacegroup_map( Workspacegroup *wsg, workspace_map_fn fn, void *a, void *b ); GType workspacegroup_get_type( void ); gboolean workspacegroup_is_empty( Workspacegroup *wsg ); Workspacegroup *workspacegroup_new( Workspaceroot *wsr ); Workspacegroup *workspacegroup_new_blank( Workspaceroot *wsr, const char *name ); Workspacegroup *workspacegroup_new_filename( Workspaceroot *wsr, const char *filename ); Workspacegroup *workspacegroup_new_from_file( Workspaceroot *wsr, const char *filename, const char *filename_user ); Workspacegroup *workspacegroup_new_from_openfile( Workspaceroot *wsr, iOpenFile *of ); gboolean workspacegroup_merge_workspaces( Workspacegroup *wsg, const char *filename ); gboolean workspacegroup_merge_columns( Workspacegroup *wsg, const char *filename ); gboolean workspacegroup_merge_rows( Workspacegroup *wsg, const char *filename ); gboolean workspacegroup_save_selected( Workspacegroup *wsg, const char *filename ); gboolean workspacegroup_save_current( Workspacegroup *wsg, const char *filename ); gboolean workspacegroup_save_all( Workspacegroup *wsg, const char *filename ); Workspacegroup *workspacegroup_duplicate( Workspacegroup *wsg ); char *workspacegroup_autosave_recover( void ); void workspacegroup_autosave_clean( void ); ================================================ FILE: src/workspacegroupview.c ================================================ /* main processing window */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #include "ip.h" /* #define DEBUG */ static ViewClass *parent_class = NULL; static void workspacegroupview_realize( GtkWidget *widget ) { #ifdef DEBUG { Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( widget ); Workspace *ws = WORKSPACE( VOBJECT( wsgview )->iobject ); printf( "workspacegroupview_realize: %s\n", IOBJECT( ws )->name ); } #endif /*DEBUG*/ GTK_WIDGET_CLASS( parent_class )->realize( widget ); /* Mark us as a symbol drag-to widget. */ set_symbol_drag_type( widget ); } static void workspacegroupview_rename_sub( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Workspace *ws = WORKSPACE( client ); Stringset *ss = STRINGSET( iwnd ); StringsetChild *name = stringset_child_get( ss, _( "Name" ) ); StringsetChild *caption = stringset_child_get( ss, _( "Caption" ) ); char name_text[1024]; char caption_text[1024]; if( !get_geditable_name( name->entry, name_text, 1024 ) || !get_geditable_string( caption->entry, caption_text, 1024 ) ) { nfn( sys, IWINDOW_ERROR ); return; } if( !workspace_rename( ws, name_text, caption_text ) ) { nfn( sys, IWINDOW_ERROR ); return; } nfn( sys, IWINDOW_YES ); } static void workspacegroupview_rename_cb( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); GtkWidget *ss = stringset_new(); if( ws->locked ) return; stringset_child_new( STRINGSET( ss ), _( "Name" ), IOBJECT( ws )->name, _( "Set tab name here" ) ); stringset_child_new( STRINGSET( ss ), _( "Caption" ), IOBJECT( ws )->caption, _( "Set tab caption here" ) ); iwindow_set_title( IWINDOW( ss ), _( "Rename Tab \"%s\"" ), IOBJECT( ws )->name ); idialog_set_callbacks( IDIALOG( ss ), iwindow_true_cb, NULL, NULL, ws ); idialog_add_ok( IDIALOG( ss ), workspacegroupview_rename_sub, _( "Rename Tab" ) ); iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( wview ) ); iwindow_build( IWINDOW( ss ) ); gtk_widget_show( ss ); } static void workspacegroupview_rename_cb2( GtkWidget *wid, GdkEvent *event, Workspaceview *wview ) { workspacegroupview_rename_cb( wid, NULL, wview ); } static void workspacegroupview_child_add( View *parent, View *child ) { Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( parent ); Workspaceview *wview = WORKSPACEVIEW( child ); Workspace *ws = WORKSPACE( VOBJECT( child )->iobject ); GtkWidget *ebox; GtkWidget *hbox; GtkWidget *label; GtkWidget *padlock; GtkWidget *alert; VIEW_CLASS( parent_class )->child_add( parent, child ); ebox = gtk_event_box_new(); gtk_widget_add_events( GTK_WIDGET( ebox ), GDK_BUTTON_PRESS_MASK ); hbox = gtk_hbox_new( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( ebox ), hbox ); gtk_widget_show( GTK_WIDGET( hbox ) ); padlock = gtk_image_new(); gtk_box_pack_start( GTK_BOX( hbox ), padlock, FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( padlock ) ); set_tooltip( padlock, "%s", _( "unlock from Edit menu" ) ); alert = gtk_image_new(); gtk_box_pack_start( GTK_BOX( hbox ), alert, FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( alert ) ); set_tooltip( alert, "%s", _( "errors in tab" ) ); label = gtk_label_new( NN( IOBJECT( ws->sym )->name ) ); gtk_box_pack_end( GTK_BOX( hbox ), label, FALSE, FALSE, 0 ); gtk_widget_show( GTK_WIDGET( label ) ); workspaceview_set_label( wview, label, padlock, alert ); popup_attach( ebox, wsgview->tab_menu, wview ); doubleclick_add( ebox, FALSE, NULL, NULL, DOUBLECLICK_FUNC( workspacegroupview_rename_cb2 ), wview ); gtk_notebook_insert_page( GTK_NOTEBOOK( wsgview->notebook ), GTK_WIDGET( wview ), ebox, ICONTAINER( ws )->pos ); gtk_notebook_set_tab_reorderable( GTK_NOTEBOOK( wsgview->notebook ), GTK_WIDGET( wview ), TRUE ); gtk_notebook_set_tab_detachable( GTK_NOTEBOOK( wsgview->notebook ), GTK_WIDGET( wview ), TRUE ); } static void workspacegroupview_child_remove( View *parent, View *child ) { /* Stuff. Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( parent ); Workspaceview *wview = WORKSPACEVIEW( child ); */ VIEW_CLASS( parent_class )->child_remove( parent, child ); } static void workspacegroupview_child_position( View *parent, View *child ) { Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( parent ); Workspaceview *wview = WORKSPACEVIEW( child ); gtk_notebook_reorder_child( GTK_NOTEBOOK( wsgview->notebook ), GTK_WIDGET( wview ), ICONTAINER( wview )->pos ); VIEW_CLASS( parent_class )->child_position( parent, child ); } static void workspacegroupview_child_front( View *parent, View *child ) { Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( parent ); Workspaceview *wview = WORKSPACEVIEW( child ); int page; GtkWidget *current_front; page = gtk_notebook_get_current_page( GTK_NOTEBOOK( wsgview->notebook ) ); current_front = gtk_notebook_get_nth_page( GTK_NOTEBOOK( wsgview->notebook ), page ); if( current_front != GTK_WIDGET( wview ) ) { page = gtk_notebook_page_num( GTK_NOTEBOOK( wsgview->notebook ), GTK_WIDGET( wview ) ); gtk_notebook_set_current_page( GTK_NOTEBOOK( wsgview->notebook ), page ); } } static void workspacegroupview_class_init( WorkspacegroupviewClass *class ) { GtkWidgetClass *widget_class = (GtkWidgetClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); widget_class->realize = workspacegroupview_realize; view_class->child_add = workspacegroupview_child_add; view_class->child_remove = workspacegroupview_child_remove; view_class->child_position = workspacegroupview_child_position; view_class->child_front = workspacegroupview_child_front; } typedef struct _nip2GtkNotebookPage { GtkWidget *child; /* A lot of stuff follows in the real struct, which we ignore. */ } nip2GtkNotebookPage; /* gtk+-2.20 and earlier had a bug whereby switch_page would be given a * GtkNotebookPage rather than the actual page widget. */ static Workspaceview * notebookpage_get_workspaceview( GtkWidget *page ) { #ifdef USE_NOTEBOOK_GROUP_NAME return( WORKSPACEVIEW( page ) ); #else /*!USE_NOTEBOOK_GROUP_NAME*/ /* Buggy argh. */ return( WORKSPACEVIEW( ((nip2GtkNotebookPage *) page)->child ) ); #endif } /* Called for switching the current page, and for page drags between * notebooks. */ static void workspacegroupview_switch_page_cb( GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data ) { Workspaceview *wview = notebookpage_get_workspaceview( page ); Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); Workspacegroup *old_wsg = WORKSPACEGROUP( ICONTAINER( ws )->parent ); Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( user_data ); Workspacegroup *wsg = WORKSPACEGROUP( VOBJECT( wsgview )->iobject ); if( ICONTAINER( ws )->parent != ICONTAINER( wsg ) ) { icontainer_reparent( ICONTAINER( wsg ), ICONTAINER( ws ), -1 ); filemodel_set_modified( FILEMODEL( wsg ), TRUE ); filemodel_set_modified( FILEMODEL( old_wsg ), TRUE ); /* If dragging the tab has emptied the old wsg, we can junk * the window. */ mainw_cull(); } icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); if( ws->compat_major ) { error_top( _( "Compatibility mode." ) ); error_sub( _( "This workspace was created by version %d.%d. " "A set of compatibility menus have been loaded " "for this window." ), ws->compat_major, ws->compat_minor ); iwindow_alert( GTK_WIDGET( wview ), GTK_MESSAGE_INFO ); } /* How bizarre, pages sometimes fail to set up correctly. Force a * resize to get everything to init. */ if( wview && wview->fixed ) gtk_container_check_resize( GTK_CONTAINER( wview->fixed ) ); } static void workspacegroupview_page_added_cb( GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data ) { Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( user_data ); Workspacegroup *wsg = WORKSPACEGROUP( VOBJECT( wsgview )->iobject ); Mainw *mainw = MAINW( iwindow_get_root( GTK_WIDGET( notebook ) ) ); filemodel_set_window_hint( FILEMODEL( wsg ), IWINDOW( mainw ) ); } static GtkNotebook * workspacegroupview_create_window_cb( GtkNotebook *notebook, GtkWidget *page, int x, int y, gpointer user_data ) { Workspaceview *wview = WORKSPACEVIEW( page ); Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); Workspacegroup *wsg = WORKSPACEGROUP( ICONTAINER( ws )->parent ); Workspaceroot *wsr = wsg->wsr; Mainw *new_mainw; Workspacegroup *new_wsg; char name[256]; /* printf( "workspacegroupview_create_window_cb: wsg = %s, ws = %s\n", NN( IOBJECT( wsg )->name ), NN( IOBJECT( ws )->name ) ); printf( "workspacegroupview_create_window_cb: x = %d, y = %d\n", x, y ); */ workspaceroot_name_new( wsr, name ); new_wsg = workspacegroup_new( wsr ); /* printf( "workspacegroupview_create_window_cb: new wsg = %s\n", name ); */ iobject_set( IOBJECT( new_wsg ), name, NULL ); new_mainw = mainw_new( new_wsg ); gtk_window_move( GTK_WINDOW( new_mainw ), x, y ); gtk_widget_show( GTK_WIDGET( new_mainw ) ); return( GTK_NOTEBOOK( new_mainw->wsgview->notebook ) ); } static void workspacegroupview_page_reordered_cb( GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data ) { Workspaceview *wview = WORKSPACEVIEW( page ); Workspacegroupview *wsgview = WORKSPACEGROUPVIEW( VIEW( wview )->parent ); Workspacegroup *wsg = WORKSPACEGROUP( VOBJECT( wsgview )->iobject ); int i; gboolean changed; changed = FALSE; for( i = 0; i < gtk_notebook_get_n_pages( notebook ); i++ ) { GtkWidget *page_n = gtk_notebook_get_nth_page( notebook, i ); Workspaceview *wview = WORKSPACEVIEW( page_n ); Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); if( ICONTAINER( ws )->pos != i ) { ICONTAINER( ws )->pos = i; changed = TRUE; } } if( changed ) { icontainer_pos_sort( ICONTAINER ( wsg ) ); filemodel_set_modified( FILEMODEL( wsg ), TRUE ); } } static void workspacegroupview_tab_double_cb( GtkNotebook *notebook, GdkEvent *event, Workspacegroupview *wsgview ) { Workspacegroup *wsg = WORKSPACEGROUP( VOBJECT( wsgview )->iobject ); int i; GtkWidget *page; GtkWidget *tab; /* Doubleclick in a tab row background. This could be the gutter or * the edge of a label. Get the position of the right-most tab and * check our click x against that. */ i = gtk_notebook_get_n_pages( notebook ); page = gtk_notebook_get_nth_page( notebook, i - 1 ); tab = gtk_notebook_get_tab_label( notebook, page ); if( event->button.x > tab->allocation.x + tab->allocation.width && !workspace_new_blank( wsg ) ) iwindow_alert( GTK_WIDGET( wsgview ), GTK_MESSAGE_ERROR ); } static void workspacegroupview_add_workspace_cb( GtkWidget *wid, Workspacegroupview *wsgview ) { Workspacegroup *wsg = WORKSPACEGROUP( VOBJECT( wsgview )->iobject ); if( !workspace_new_blank( wsg ) ) iwindow_alert( GTK_WIDGET( wsgview ), GTK_MESSAGE_ERROR ); } static void workspacegroupview_add_workspace_cb2( GtkWidget *wid, GtkWidget *host, Workspacegroupview *wsgview ) { workspacegroupview_add_workspace_cb( wid, wsgview ); } static void workspacegroupview_load_workspace_cb2( GtkWidget *wid, GtkWidget *host, Workspacegroupview *wsgview ) { Mainw *mainw = MAINW( iwindow_get_root( GTK_WIDGET( wsgview ) ) ); mainw_workspace_merge( mainw ); } static void workspacegroupview_select_all_cb( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); if( !ws->locked ) workspace_select_all( ws ); } static void workspacegroupview_duplicate_cb( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); if( !workspace_duplicate( ws ) ) { iwindow_alert( host, GTK_MESSAGE_ERROR ); return; } } static void workspacegroupview_merge_sub( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Workspace *ws = WORKSPACE( client ); Workspacegroup *wsg = workspace_get_workspacegroup( ws ); char *filename; Column *col; if( (filename = filesel_get_filename( filesel )) ) { icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); progress_begin(); column_clear_last_new(); if( !workspace_merge_file( ws, filename ) ) nfn( sys, IWINDOW_ERROR ); else { symbol_recalculate_all(); nfn( sys, IWINDOW_YES ); } if( (col = column_get_last_new()) ) column_scrollto( col, MODEL_SCROLL_TOP ); progress_end(); g_free( filename ); } else nfn( sys, IWINDOW_ERROR ); } static void workspacegroupview_merge_cb( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( wview ) ) ); GtkWidget *filesel = filesel_new(); if( ws->locked ) return; iwindow_set_title( IWINDOW( filesel ), _( "Merge Into Tab \"%s\"" ), IOBJECT( ws )->name ); filesel_set_flags( FILESEL( filesel ), FALSE, FALSE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) ); idialog_set_iobject( IDIALOG( filesel ), IOBJECT( ws ) ); filesel_set_done( FILESEL( filesel ), workspacegroupview_merge_sub, ws ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } static void workspacegroupview_save_as_sub( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { Filesel *filesel = FILESEL( iwnd ); Workspace *ws = WORKSPACE( client ); Workspacegroup *wsg = workspace_get_workspacegroup( ws ); char *filename; if( (filename = filesel_get_filename( filesel )) ) { icontainer_current( ICONTAINER( wsg ), ICONTAINER( ws ) ); if( !workspacegroup_save_current( wsg, filename ) ) nfn( sys, IWINDOW_ERROR ); else nfn( sys, IWINDOW_YES ); g_free( filename ); } else nfn( sys, IWINDOW_ERROR ); } static void workspacegroupview_save_as_cb( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( wview ) ) ); GtkWidget *filesel = filesel_new(); iwindow_set_title( IWINDOW( filesel ), _( "Save Tab \"%s\"" ), IOBJECT( ws )->name ); filesel_set_flags( FILESEL( filesel ), FALSE, TRUE ); filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 ); iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) ); idialog_set_iobject( IDIALOG( filesel ), IOBJECT( ws ) ); filesel_set_done( FILESEL( filesel ), workspacegroupview_save_as_sub, ws ); iwindow_build( IWINDOW( filesel ) ); gtk_widget_show( GTK_WIDGET( filesel ) ); } /* ws has been destroyed. */ static void workspacegroupview_delete_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys ) { mainw_cull(); nfn( sys, IWINDOW_YES ); } static void workspacegroupview_delete_cb( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); if( !ws->locked ) model_check_destroy( view_get_toplevel( VIEW( wview ) ), MODEL( ws ), workspacegroupview_delete_done_cb ); } static void workspacegroupview_init( Workspacegroupview *wsgview ) { wsgview->notebook = gtk_notebook_new(); gtk_notebook_set_scrollable( GTK_NOTEBOOK( wsgview->notebook ), TRUE ); #ifdef USE_NOTEBOOK_GROUP_NAME gtk_notebook_set_group_name( GTK_NOTEBOOK( wsgview->notebook ), "wsgview" ); #endif /*USE_NOTEBOOK_GROUP_NAME*/ gtk_notebook_set_tab_pos( GTK_NOTEBOOK( wsgview->notebook ), GTK_POS_TOP ); g_signal_connect( wsgview->notebook, "switch_page", G_CALLBACK( workspacegroupview_switch_page_cb ), wsgview ); g_signal_connect( wsgview->notebook, "page_added", G_CALLBACK( workspacegroupview_page_added_cb ), wsgview ); g_signal_connect( wsgview->notebook, "page_reordered", G_CALLBACK( workspacegroupview_page_reordered_cb ), wsgview ); g_signal_connect( wsgview->notebook, "create_window", G_CALLBACK( workspacegroupview_create_window_cb ), wsgview ); doubleclick_add( wsgview->notebook, FALSE, NULL, NULL, DOUBLECLICK_FUNC( workspacegroupview_tab_double_cb ), wsgview ); wsgview->gutter_menu = popup_build( _( "Tab gutter menu" ) ); popup_add_but( wsgview->gutter_menu, _( "New Tab" ), POPUP_FUNC( workspacegroupview_add_workspace_cb2 ) ); popup_add_but( wsgview->gutter_menu, _( "Merge Into Workspace" ), POPUP_FUNC( workspacegroupview_load_workspace_cb2 ) ); popup_attach( wsgview->notebook, wsgview->gutter_menu, wsgview ); #ifdef USE_NOTEBOOK_ACTION { GtkWidget *but; GtkWidget *icon; but = gtk_button_new(); gtk_button_set_relief( GTK_BUTTON( but ), GTK_RELIEF_NONE ); set_tooltip( but, _( "Add a workspace" ) ); icon = gtk_image_new_from_stock( GTK_STOCK_ADD, GTK_ICON_SIZE_MENU ); gtk_container_add( GTK_CONTAINER( but ), icon ); gtk_widget_show( icon ); gtk_widget_show( but ); gtk_notebook_set_action_widget( GTK_NOTEBOOK( wsgview->notebook ), but, GTK_PACK_END ); g_signal_connect( but, "clicked", G_CALLBACK( workspacegroupview_add_workspace_cb ), wsgview ); } #endif /*USE_NOTEBOOK_ACTION*/ gtk_box_pack_start( GTK_BOX( wsgview ), wsgview->notebook, TRUE, TRUE, 0 ); gtk_widget_show( wsgview->notebook ); wsgview->tab_menu = popup_build( _( "Tab menu" ) ); popup_add_but( wsgview->tab_menu, _( "Rename" ), POPUP_FUNC( workspacegroupview_rename_cb ) ); popup_add_but( wsgview->tab_menu, _( "Select All" ), POPUP_FUNC( workspacegroupview_select_all_cb ) ); popup_add_but( wsgview->tab_menu, STOCK_DUPLICATE, POPUP_FUNC( workspacegroupview_duplicate_cb ) ); popup_add_but( wsgview->tab_menu, _( "Merge Into Tab" ), POPUP_FUNC( workspacegroupview_merge_cb ) ); popup_add_but( wsgview->tab_menu, GTK_STOCK_SAVE_AS, POPUP_FUNC( workspacegroupview_save_as_cb ) ); menu_add_sep( wsgview->tab_menu ); popup_add_but( wsgview->tab_menu, GTK_STOCK_DELETE, POPUP_FUNC( workspacegroupview_delete_cb ) ); } GtkType workspacegroupview_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( WorkspacegroupviewClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) workspacegroupview_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Workspacegroupview ), 32, /* n_preallocs */ (GInstanceInitFunc) workspacegroupview_init, }; type = g_type_register_static( TYPE_VIEW, "Workspacegroupview", &info, 0 ); } return( type ); } View * workspacegroupview_new( void ) { Workspacegroupview *wsgview = gtk_type_new( TYPE_WORKSPACEGROUPVIEW ); return( VIEW( wsgview ) ); } ================================================ FILE: src/workspacegroupview.h ================================================ /* A view for a Workspacegroup (a set of workspaces) ... display as a notebook. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_WORKSPACEGROUPVIEW (workspacegroupview_get_type()) #define WORKSPACEGROUPVIEW( obj ) (GTK_CHECK_CAST( (obj), \ TYPE_WORKSPACEGROUPVIEW, Workspacegroupview )) #define WORKSPACEGROUPVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_WORKSPACEGROUPVIEW, WorkspacegroupviewClass )) #define IS_WORKSPACEGROUPVIEW( obj ) (GTK_CHECK_TYPE( (obj), \ TYPE_WORKSPACEGROUPVIEW )) #define IS_WORKSPACEGROUPVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_WORKSPACEGROUPVIEW )) struct _Workspacegroupview { View parent_object; GtkWidget *tab_menu; GtkWidget *gutter_menu; GtkWidget *notebook; }; typedef struct _WorkspacegroupviewClass { ViewClass parent_class; /* My methods. */ } WorkspacegroupviewClass; GType workspacegroupview_get_type( void ); View *workspacegroupview_new( void ); ================================================ FILE: src/workspaceroot.c ================================================ /* The root of all workspaces. A singleton all workspaces are children of. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #include "ip.h" static ModelClass *parent_class = NULL; static void workspaceroot_dispose( GObject *gobject ) { Workspaceroot *wsr; #ifdef DEBUG printf( "workspaceroot_dispose\n" ); #endif /*DEBUG*/ g_return_if_fail( gobject != NULL ); g_return_if_fail( IS_WORKSPACEROOT( gobject ) ); wsr = WORKSPACEROOT( gobject ); wsr->sym = NULL; G_OBJECT_CLASS( parent_class )->dispose( gobject ); } static void workspaceroot_child_add( iContainer *parent, iContainer *child, int pos ) { ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos ); #ifdef DEBUG printf( "workspaceroot_child_add: added %s\n", IOBJECT( child )->name ); #endif /*DEBUG*/ } static void workspaceroot_child_remove( iContainer *parent, iContainer *child ) { ICONTAINER_CLASS( parent_class )->child_remove( parent, child ); } static void workspaceroot_class_init( WorkspacerootClass *class ) { GObjectClass *gobject_class = (GObjectClass *) class; iContainerClass *icontainer_class = (iContainerClass *) class; parent_class = g_type_class_peek_parent( class ); /* Create signals. */ /* Init methods. */ gobject_class->dispose = workspaceroot_dispose; icontainer_class->child_add = workspaceroot_child_add; icontainer_class->child_remove = workspaceroot_child_remove; } static void workspaceroot_init( Workspaceroot *wsr ) { wsr->sym = NULL; } GType workspaceroot_get_type( void ) { static GType type = 0; if( !type ) { static const GTypeInfo info = { sizeof( WorkspacerootClass ), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) workspaceroot_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof( Workspaceroot ), 32, /* n_preallocs */ (GInstanceInitFunc) workspaceroot_init, }; type = g_type_register_static( TYPE_MODEL, "Workspaceroot", &info, 0 ); } return( type ); } static void workspaceroot_link( Workspaceroot *wsr, const char *name ) { Symbol *sym; iobject_set( IOBJECT( wsr ), name, NULL ); wsr->sym = sym = symbol_new( symbol_root->expr->compile, name ); sym->type = SYM_WORKSPACEROOT; sym->wsr = wsr; sym->expr = expr_new( sym ); (void) compile_new( sym->expr ); symbol_made( sym ); } Workspaceroot * workspaceroot_new( const char *name ) { Workspaceroot *wsr; if( compile_lookup( symbol_root->expr->compile, name ) ) { error_top( _( "Name clash." ) ); error_sub( _( "Can't create workspaceroot \"%s\". " "A symbol with that name already exists." ), name ); return( NULL ); } wsr = WORKSPACEROOT( g_object_new( TYPE_WORKSPACEROOT, NULL ) ); workspaceroot_link( wsr, name ); return( wsr ); } /* Make up a new workspace name. */ void workspaceroot_name_new( Workspaceroot *wsr, char *name ) { Compile *compile = wsr->sym->expr->compile; strcpy( name, "tab1" ); while( compile_lookup( compile, name ) ) increment_name( name ); } ================================================ FILE: src/workspaceroot.h ================================================ /* The root of all workspaces. A singleton all workspaces are children of. */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_WORKSPACEROOT (workspaceroot_get_type()) #define WORKSPACEROOT( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), TYPE_WORKSPACEROOT, \ Workspaceroot )) #define WORKSPACEROOT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), TYPE_WORKSPACEROOT, \ WorkspacerootClass)) #define IS_WORKSPACEROOT( obj ) \ (G_TYPE_CHECK_INSTANCE_TYPE( (obj), TYPE_WORKSPACEROOT )) #define IS_WORKSPACEROOT_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_TYPE( (klass), TYPE_WORKSPACEROOT )) #define WORKSPACEROOT_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), TYPE_WORKSPACEROOT, \ WorkspacerootClass )) /* A workspaceroot. */ struct _Workspaceroot { Model parent_object; Symbol *sym; /* Workspace in this group in this */ }; typedef struct _WorkspacerootClass { ModelClass parent_class; /* Methods. */ } WorkspacerootClass; GType workspaceroot_get_type( void ); Workspaceroot *workspaceroot_new( const char *name ); void workspaceroot_name_new( Workspaceroot *wsr, char *name ); ================================================ FILE: src/workspaceview.c ================================================ /* a workspaceview button in a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ /* Define to trace button press events. #define EVENT */ #include "ip.h" static ViewClass *parent_class = NULL; /* Params for "Align Columns" function. */ static const int workspaceview_layout_snap_threshold = 30; static const int workspaceview_layout_hspacing = 10; static const int workspaceview_layout_vspacing = 10; static const int workspaceview_layout_left = WORKSPACEVIEW_MARGIN_LEFT; static const int workspaceview_layout_top = WORKSPACEVIEW_MARGIN_TOP; static void workspaceview_scroll_to( Workspaceview *wview, int x, int y ) { GtkAdjustment *hadj = gtk_scrolled_window_get_hadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); int nx, ny; nx = IM_CLIP( 0, x, wview->width - wview->vp.width ); ny = IM_CLIP( 0, y, wview->height - wview->vp.height ); adjustments_set_value( hadj, vadj, nx, ny ); } /* Scroll by an amount horizontally and vertically. */ static void workspaceview_displace( Workspaceview *wview, int u, int v ) { workspaceview_scroll_to( wview, wview->vp.left + u, wview->vp.top + v ); } /* Scroll to make an xywh area visible. If the area is larger than the * viewport, position the view at the bottom left if the xywh area ... * this is usually right for workspaces. */ void workspaceview_scroll( Workspaceview *wview, int x, int y, int w, int h ) { GtkAdjustment *hadj = gtk_scrolled_window_get_hadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); Rect *vp = &wview->vp; int nx, ny; nx = hadj->value; if( x + w > IM_RECT_RIGHT( vp ) ) nx = IM_MAX( 0, (x + w) - vp->width ); if( x < nx ) nx = x; ny = vadj->value; if( y + h > IM_RECT_BOTTOM( vp ) ) ny = IM_MAX( 0, (y + h) - vp->height ); if( y < ny ) ny = y; #ifdef DEBUG printf( "workspaceview_scroll: x=%d, y=%d, w=%d, h=%d, " "nx = %d, ny = %d\n", x, y, w, h, nx, ny ); #endif /*DEBUG*/ adjustments_set_value( hadj, vadj, nx, ny ); } /* Update our geometry from the fixed widget. */ static void workspaceview_scroll_update( Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); GtkAdjustment *hadj = gtk_scrolled_window_get_hadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); wview->vp.left = hadj->value; wview->vp.top = vadj->value; wview->vp.width = hadj->page_size; wview->vp.height = vadj->page_size; wview->width = hadj->upper; wview->height = vadj->upper; /* Update vp hint in model too. */ ws->vp = wview->vp; #ifdef DEBUG printf( "workspaceview_scroll_update: %s\n", IOBJECT( ws )->name ); printf( " wview->vp: l=%d, t=%d, w=%d, h=%d; fixed w=%d; h=%d\n", wview->vp.left, wview->vp.top, wview->vp.width, wview->vp.height, wview->width, wview->height ); #endif /*DEBUG*/ } static void workspaceview_watch_changed_cb( Watchgroup *watchgroup, Watch *watch, Workspaceview *wview ) { /* Names of prefs we watch. These are really rowview preferences, but * we follow them here to prevent every rowview having to have it's * own connection. */ static char *watch_names[] = { "CALC_DISPLAY_LED" }; int i; for( i = 0; i < IM_NUMBER( watch_names ); i++ ) if( strcmp( IOBJECT( watch )->name, watch_names[i] ) == 0 ) { view_map_all( VIEW( wview ), (view_map_fn) vobject_refresh_queue, NULL ); break; } } /* Scroll events ... handle mousewheel shortcuts here. Do this ourselves * (rather than just relying on the scrollbars) so we can do shift + wheel == * left/right. */ static gboolean workspaceview_scroll_event_cb( GtkWidget *widget, GdkEventScroll *ev, Workspaceview *wview ) { gboolean handled = FALSE; /* Gimp uses page_incr / 4 I think, but then scroll speed varies with * window size, which is pretty odd. Just use a constant. */ const int incr = 50; if( ev->direction == GDK_SCROLL_UP || ev->direction == GDK_SCROLL_DOWN ) { if( ev->state & GDK_SHIFT_MASK ) { if( ev->direction == GDK_SCROLL_UP ) workspaceview_scroll_to( wview, wview->vp.left + incr, wview->vp.top ); else workspaceview_scroll_to( wview, wview->vp.left - incr, wview->vp.top ); handled = TRUE; } else { if( ev->direction == GDK_SCROLL_UP ) workspaceview_scroll_to( wview, wview->vp.left, wview->vp.top - incr ); else workspaceview_scroll_to( wview, wview->vp.left, wview->vp.top + incr ); handled = TRUE; } } return( handled ); } static void workspaceview_realize_cb( GtkWidget *wid, Workspaceview *wview ) { g_assert( wid->window ); gtk_widget_add_events( wid, GDK_BUTTON_PRESS_MASK ); } void workspaceview_set_cursor( Workspaceview *wview, iWindowShape shape ) { if( !wview->context ) wview->context = iwindow_cursor_context_new( IWINDOW( view_get_toplevel( VIEW( wview ) ) ), 0, "workspaceview" ); iwindow_cursor_context_set_cursor( wview->context, shape ); } typedef struct _WorkspaceviewFindColumnview { Workspaceview *wview; int x; int y; } WorkspaceviewFindColumnview; static void * workspaceview_find_columnview_sub( View *view, WorkspaceviewFindColumnview *args ) { Columnview *cview = COLUMNVIEW( view ); Rect col; int x, y, w, h; columnview_get_position( cview, &x, &y, &w, &h ); col.left = x; col.top = y; col.width = w; col.height = h; if( im_rect_includespoint( &col, args->x, args->y ) ) return( cview ); return( NULL ); } /* Test for a point is workspaceview background ... ie. is not enclosed by one * of our columns. */ static Columnview * workspaceview_find_columnview( Workspaceview *wview, int x, int y ) { WorkspaceviewFindColumnview args; void *res; args.wview = wview; args.x = x; args.y = y; res = view_map( VIEW( wview ), (view_map_fn) workspaceview_find_columnview_sub, &args, NULL ); if( res ) return( COLUMNVIEW( res ) ); else return( NULL ); } /* Is this event on the workspaceview background. */ static gboolean workspaceview_is_background( Workspaceview *wview, GdkWindow *window, int x, int y ) { /* If the event window is not our window, it must have occured in a * sub-GdkWindow (eg. an image thumbnail), so can't be a background * click. */ if( window != wview->fixed->window ) return( FALSE ); /* Could be a click in a non-window widget (eg. a label); search * all columnviews for a hit. */ return( !workspaceview_find_columnview( wview, x, y ) ); } static gboolean workspaceview_fixed_event_cb( GtkWidget *widget, GdkEvent *ev, Workspaceview *wview ) { gboolean handled = FALSE; #ifdef EVENT printf( "workspaceview_fixed_event_cb: %d\n", ev->type ); #endif /*EVENT*/ switch( ev->type ) { case GDK_BUTTON_PRESS: if( ev->button.button == 1 ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); if( workspaceview_is_background( wview, ev->button.window, ev->button.x, ev->button.y ) ) { workspace_deselect_all( ws ); handled = TRUE; } } else if( ev->button.button == 2 ) { #ifdef EVENT printf( "workspaceview_fixed_event_cb: start drag\n" ); #endif /*EVENT*/ wview->drag_x = ev->button.x_root + wview->vp.left; wview->drag_y = ev->button.y_root + wview->vp.top; workspaceview_set_cursor( wview, IWINDOW_SHAPE_MOVE ); wview->dragging = TRUE; handled = TRUE; } break; case GDK_BUTTON_RELEASE: if( ev->button.button == 2 ) { #ifdef EVENT printf( "workspaceview_fixed_event_cb: stop drag\n" ); #endif /*EVENT*/ workspaceview_set_cursor( wview, IWINDOW_SHAPE_NONE ); wview->dragging = FALSE; handled = TRUE; } break; case GDK_MOTION_NOTIFY: if( wview->dragging && ev->motion.state & GDK_BUTTON2_MASK ) { #ifdef EVENT printf( "workspaceview_fixed_event_cb: motion\n" ); #endif /*EVENT*/ /* We're using hints. */ widget_update_pointer( GTK_WIDGET( wview ), ev ); workspaceview_scroll_to( wview, wview->drag_x - ev->motion.x_root, wview->drag_y - ev->motion.y_root ); handled = TRUE; } break; default: break; } return( handled ); } static void workspaceview_scroll_adjustment_cb( GtkAdjustment *adj, Workspaceview *wview ) { workspaceview_scroll_update( wview ); } /* Timer callback for background scroll. */ static gboolean workspaceview_scroll_time_cb( Workspaceview *wview ) { /* Perform scroll. */ workspaceview_scroll_update( wview ); if( wview->u != 0 || wview->v != 0 ) workspaceview_displace( wview, wview->u, wview->v ); /* Start timer again. */ return( TRUE ); } /* Stop the tally_scroll timer. */ static void workspaceview_scroll_stop( Workspaceview *wview ) { IM_FREEF( g_source_remove, wview->timer ); } /* Start the tally_scroll timer. */ static void workspaceview_scroll_start( Workspaceview *wview ) { workspaceview_scroll_stop( wview ); wview->timer = g_timeout_add( 30, (GSourceFunc) workspaceview_scroll_time_cb, wview ); } /* Set a background scroll. Pass both zero to stop scroll. */ void workspaceview_scroll_background( Workspaceview *wview, int u, int v ) { wview->u = u; wview->v = v; if( u == 0 && v == 0 ) workspaceview_scroll_stop( wview ); else workspaceview_scroll_start( wview ); } static void workspaceview_destroy( GtkObject *object ) { Workspaceview *wview; #ifdef DEBUG printf( "workspaceview_destroy: %p\n", object ); #endif /*DEBUG*/ g_return_if_fail( object != NULL ); g_return_if_fail( IS_WORKSPACEVIEW( object ) ); wview = WORKSPACEVIEW( object ); /* Instance destroy. */ workspaceview_scroll_stop( wview ); IM_FREEF( iwindow_cursor_context_destroy, wview->context ); FREESID( wview->watch_changed_sid, main_watchgroup ); DESTROY_GTK( wview->popup ); GTK_OBJECT_CLASS( parent_class )->destroy( object ); } static void workspaceview_realize( GtkWidget *widget ) { #ifdef DEBUG { Workspaceview *wview = WORKSPACEVIEW( widget ); Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); printf( "workspaceview_realize: %s\n", IOBJECT( ws )->name ); } #endif /*DEBUG*/ GTK_WIDGET_CLASS( parent_class )->realize( widget ); /* Mark us as a symbol drag-to widget. */ set_symbol_drag_type( widget ); } static void workspaceview_drag_data_received( GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time ) { Workspaceview *wview = WORKSPACEVIEW( widget ); Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); const char *from_row_path = (const char *) selection_data->data; Row *from_row; #ifdef DEBUG printf( "workspaceview_drag_data_received:\n" ); #endif /*DEBUG*/ /* We seem to rx drag events with x/y relative to the viewport. */ x += wview->vp.left; y += wview->vp.top; if( info == TARGET_SYMBOL && selection_data->length > 0 && selection_data->format == 8 && workspaceview_is_background( wview, GTK_WIDGET( wview->fixed )->window, x, y ) && (from_row = row_parse_name( main_workspaceroot->sym, from_row_path )) ) { char new_name[MAX_STRSIZE]; Column *col; char vips_buf_text[256]; VipsBuf buf = VIPS_BUF_STATIC( vips_buf_text ); Symbol *sym; workspace_column_name_new( ws, new_name ); col = column_new( ws, new_name ); col->x = x; col->y = y; workspace_column_select( ws, col ); /* Qualify relative to us. We don't want to embed * workspace names unless we have to. */ row_qualified_name_relative( ws->sym, from_row, &buf ); if( !(sym = workspace_add_def( ws, vips_buf_all( &buf ) )) ) iwindow_alert( widget, GTK_MESSAGE_ERROR ); symbol_recalculate_all(); /* Usually the drag-from row will be selected, very * annoying. Select the drag-to row. */ if( sym && sym->expr && sym->expr->row ) row_select( sym->expr->row ); } } static void * workspaceview_child_size_sub( Columnview *cview, Rect *area ) { int x, y, w, h; Rect col; columnview_get_position( cview, &x, &y, &w, &h ); col.left = x; col.top = y; col.width = w; col.height = h; im_rect_unionrect( area, &col, area ); return( NULL ); } static void workspaceview_child_size_cb( Columnview *cview, GtkAllocation *allocation, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); Workspacegroup *wsg = workspace_get_workspacegroup( ws ); int right, bottom; g_assert( IS_WORKSPACEVIEW( wview ) ); /* Compute a new bounding box for our children. */ wview->bounding.left = 0; wview->bounding.top = 0; wview->bounding.width = 0; wview->bounding.height = 0; (void) view_map( VIEW( wview ), (view_map_fn) workspaceview_child_size_sub, &wview->bounding, NULL ); wview->bounding.width += 1000; wview->bounding.height += 1000; #ifdef DEBUG { Column *col = COLUMN( VOBJECT( cview )->iobject ); printf( "workspaceview_child_size_cb: cview %s " "bb left=%d, top=%d, width=%d, height=%d\n", IOBJECT( col )->name, wview->bounding.left, wview->bounding.top, wview->bounding.width, wview->bounding.height ); } #endif /*DEBUG*/ /* Resize our fixed if necessary. */ right = IM_RECT_RIGHT( &wview->bounding ); bottom = IM_RECT_BOTTOM( &wview->bounding ); if( right != wview->width || bottom != wview->height ) { gtk_widget_set_size_request( GTK_WIDGET( wview->fixed ), right, bottom ); /* Update the model hints ... it uses bounding to position * loads and saves. */ ws->area = wview->bounding; filemodel_set_offset( FILEMODEL( wsg ), ws->area.left, ws->area.top ); } } /* Pick an xy position for the next column. */ static void workspaceview_pick_xy( Workspaceview *wview, int *x, int *y ) { /* Position already set? No change. */ if( *x >= 0 ) return; /* Set this position. */ *x = wview->next_x + wview->vp.left; *y = wview->next_y + wview->vp.top; /* And move on. */ wview->next_x += 30; wview->next_y += 30; if( wview->next_x > 300 ) wview->next_x = 3; if( wview->next_y > 200 ) wview->next_y = 3; } static void workspaceview_link( View *view, Model *model, View *parent ) { Workspaceview *wview = WORKSPACEVIEW( view ); Workspace *ws = WORKSPACE( model ); VIEW_CLASS( parent_class )->link( view, model, parent ); vobject_link( VOBJECT( wview->toolkitbrowser ), IOBJECT( ws->kitg ) ); vobject_link( VOBJECT( wview->workspacedefs ), IOBJECT( ws ) ); toolkitbrowser_set_workspace( wview->toolkitbrowser, ws ); pane_set_state( wview->rpane, ws->rpane_open, ws->rpane_position ); pane_set_state( wview->lpane, ws->lpane_open, ws->lpane_position ); } static void workspaceview_child_add( View *parent, View *child ) { Columnview *cview = COLUMNVIEW( child ); Column *column = COLUMN( VOBJECT( cview )->iobject ); Workspaceview *wview = WORKSPACEVIEW( parent ); gtk_signal_connect( GTK_OBJECT( child ), "size_allocate", GTK_SIGNAL_FUNC( workspaceview_child_size_cb ), parent ); VIEW_CLASS( parent_class )->child_add( parent, child ); /* Pick start xy pos. */ workspaceview_pick_xy( wview, &column->x, &column->y ); gtk_fixed_put( GTK_FIXED( wview->fixed ), GTK_WIDGET( cview ), column->x, column->y ); cview->lx = column->x; cview->ly = column->y; } static void workspaceview_child_position( View *parent, View *child ) { Workspaceview *wview = WORKSPACEVIEW( parent ); Columnview *cview = COLUMNVIEW( child ); gtk_fixed_move( GTK_FIXED( wview->fixed ), GTK_WIDGET( cview ), cview->lx, cview->ly ); VIEW_CLASS( parent_class )->child_position( parent, child ); } static void workspaceview_child_front( View *parent, View *child ) { Workspaceview *wview = WORKSPACEVIEW( parent ); Columnview *cview = COLUMNVIEW( child ); gtk_widget_ref( GTK_WIDGET( cview ) ); gtk_container_remove( GTK_CONTAINER( wview->fixed ), GTK_WIDGET( cview ) ); gtk_fixed_put( GTK_FIXED( wview->fixed ), GTK_WIDGET( cview ), cview->lx, cview->ly ); gtk_widget_unref( GTK_WIDGET( cview ) ); } static void workspaceview_refresh( vObject *vobject ) { Workspaceview *wview = WORKSPACEVIEW( vobject ); Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); #ifdef DEBUG printf( "workspaceview_refresh: %p %s\n", ws, IOBJECT( ws )->name ); #endif /*DEBUG*/ gtk_widget_set_sensitive( GTK_WIDGET( wview ), !ws->locked ); workspace_jump_update( ws, wview->popup_jump ); if( ws->rpane_open && !wview->rpane->open ) pane_animate_open( wview->rpane ); if( !ws->rpane_open && wview->rpane->open ) pane_animate_closed( wview->rpane ); if( ws->lpane_open && !wview->lpane->open ) pane_animate_open( wview->lpane ); if( !ws->lpane_open && wview->lpane->open ) pane_animate_closed( wview->lpane ); if( wview->label ) { gtk_label_set_text( GTK_LABEL( wview->label ), IOBJECT( ws )->name ); if( IOBJECT( ws )->caption ) set_tooltip( wview->label, "%s", IOBJECT( ws )->caption ); if( ws->locked ) gtk_image_set_from_stock( GTK_IMAGE( wview->padlock ), STOCK_LOCK, GTK_ICON_SIZE_MENU ); else gtk_image_clear( GTK_IMAGE( wview->padlock ) ); if( ws->errors ) gtk_image_set_from_stock( GTK_IMAGE( wview->alert ), STOCK_ALERT, GTK_ICON_SIZE_MENU ); else gtk_image_clear( GTK_IMAGE( wview->alert ) ); } VOBJECT_CLASS( parent_class )->refresh( vobject ); } /* What we track during a layout. */ typedef struct _WorkspaceLayout { /* Context. */ Workspaceview *wview; /* Set of columnviews still to be laid out. */ GSList *undone_columns; /* Track the current set of columns here. */ GSList *current_columns; /* Current position for write. */ int out_x, out_y; /* Accumulate the size of the current set of columns here. */ Rect area; /* Track the current columnview here. */ Columnview *cview; } WorkspaceLayout; static void * workspaceview_layout_add( View *view, WorkspaceLayout *layout ) { layout->undone_columns = g_slist_prepend( layout->undone_columns, view ); return( NULL ); } static void * workspaceview_layout_find_leftmost( Columnview *cview, WorkspaceLayout *layout ) { if( GTK_WIDGET( cview )->allocation.x < layout->area.left ) { layout->area.left = GTK_WIDGET( cview )->allocation.x; layout->cview = cview; } return( NULL ); } static void * workspaceview_layout_find_similar_x( Columnview *cview, WorkspaceLayout *layout ) { int x = GTK_WIDGET( cview )->allocation.x; gboolean snap; snap = FALSE; /* Special case: a colum at zero makes a new column on the far left. */ if( layout->area.left == 0 && x == 0 ) snap = TRUE; if( layout->area.left > 0 && ABS( x - layout->area.left ) < workspaceview_layout_snap_threshold ) snap = TRUE; if( snap ) { layout->current_columns = g_slist_prepend( layout->current_columns, cview ); layout->area.width = IM_MAX( layout->area.width, GTK_WIDGET( cview )->allocation.width ); } return( NULL ); } /* Compare func for row recomp sort. */ static int workspaceview_layout_sort_y( Columnview *a, Columnview *b ) { return( GTK_WIDGET( a )->allocation.y - GTK_WIDGET( b )->allocation.y ); } static void * workspaceview_layout_set_pos( Columnview *cview, WorkspaceLayout *layout ) { Column *column = COLUMN( VOBJECT( cview )->iobject ); gboolean changed; changed = FALSE; /* If this column is being dragged, put the xy we allocate into the * shadow instead. */ if( cview->shadow ) { if( cview->shadow->lx != layout->out_x || cview->shadow->ly != layout->out_y ) { cview->shadow->lx = layout->out_x; cview->shadow->ly = layout->out_y; changed = TRUE; } } else { if( column->x != layout->out_x || column->y != layout->out_y ) { column->x = layout->out_x; column->y = layout->out_y; changed = TRUE; } } layout->out_y += GTK_WIDGET( cview )->allocation.height + workspaceview_layout_vspacing; if( changed ) iobject_changed( IOBJECT( column ) ); return( NULL ); } static void * workspaceview_layout_strike( Columnview *cview, WorkspaceLayout *layout ) { layout->undone_columns = g_slist_remove( layout->undone_columns, cview ); return( NULL ); } static void workspaceview_layout_loop( WorkspaceLayout *layout ) { layout->cview = NULL; layout->area.left = INT_MAX; slist_map( layout->undone_columns, (SListMapFn) workspaceview_layout_find_leftmost, layout ); layout->current_columns = NULL; layout->area.width = GTK_WIDGET( layout->cview )->allocation.width; slist_map( layout->undone_columns, (SListMapFn) workspaceview_layout_find_similar_x, layout ); layout->current_columns = g_slist_sort( layout->current_columns, (GCompareFunc) workspaceview_layout_sort_y ); layout->out_y = workspaceview_layout_top; slist_map( layout->current_columns, (SListMapFn) workspaceview_layout_set_pos, layout ); layout->out_x += layout->area.width + workspaceview_layout_hspacing; slist_map( layout->current_columns, (SListMapFn) workspaceview_layout_strike, layout ); IM_FREEF( g_slist_free, layout->current_columns ); } /* Autolayout ... try to rearrange columns so they don't overlap. Strategy: search for left-most column search for all columns with a 'small' overlap lay those columns out vertically with some space between them ... keep the vertical ordering we had before find the width of the widest, move output over that much strike that set of columns from the list of columns to be laid out */ static void workspaceview_layout( View *view ) { Workspaceview *wview = WORKSPACEVIEW( view ); WorkspaceLayout layout; layout.wview = wview; layout.undone_columns = NULL; layout.current_columns = NULL; layout.out_x = workspaceview_layout_left; view_map( VIEW( wview ), (view_map_fn) workspaceview_layout_add, &layout, NULL ); while( layout.undone_columns ) workspaceview_layout_loop( &layout ); } static void workspaceview_class_init( WorkspaceviewClass *class ) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkWidgetClass *widget_class = (GtkWidgetClass *) class; vObjectClass *vobject_class = (vObjectClass *) class; ViewClass *view_class = (ViewClass *) class; parent_class = g_type_class_peek_parent( class ); object_class->destroy = workspaceview_destroy; widget_class->realize = workspaceview_realize; widget_class->drag_data_received = workspaceview_drag_data_received; vobject_class->refresh = workspaceview_refresh; view_class->link = workspaceview_link; view_class->child_add = workspaceview_child_add; view_class->child_position = workspaceview_child_position; view_class->child_front = workspaceview_child_front; view_class->layout = workspaceview_layout; } /* Can't use main_load(), we want to select wses after load. */ static gboolean workspaceview_load( Workspace *ws, const char *filename ) { Workspacegroup *wsg = workspace_get_workspacegroup( ws ); Workspaceroot *wsr = wsg->wsr; Workspacegroup *new_wsg; if( (new_wsg = mainw_open_workspace( wsr, filename )) ) return( TRUE ); error_clear(); /* workspace_load_file() needs to recalc to work, try to avoid that by * doing .defs first. */ if( is_file_type( &filesel_dfile_type, filename ) ) { if( toolkit_new_from_file( main_toolkitgroup, filename ) ) return( TRUE ); error_clear(); } /* Try as matrix or image. Have to do these via definitions. */ if( workspace_load_file( ws, filename ) ) return( TRUE ); error_clear(); error_top( _( "Unknown file type." ) ); error_sub( _( "Unable to load \"%s\"." ), filename ); return( FALSE ); } static void workspaceview_lpane_changed_cb( Pane *pane, Workspaceview *wview ) { Workspace *ws; if( (ws = WORKSPACE( VOBJECT( wview )->iobject )) ) if( ws->lpane_open != pane->open || ws->lpane_position != pane->user_position ) { ws->lpane_open = pane->open; ws->lpane_position = pane->user_position; prefs_set( "WORKSPACE_LPANE_OPEN", "%d", ws->lpane_open ); prefs_set( "WORKSPACE_LPANE_POSITION", "%d", ws->lpane_position ); iobject_changed( IOBJECT( ws ) ); } } static void workspaceview_rpane_changed_cb( Pane *pane, Workspaceview *wview ) { Workspace *ws; if( (ws = WORKSPACE( VOBJECT( wview )->iobject )) ) if( ws->rpane_open != pane->open || ws->rpane_position != pane->user_position ) { ws->rpane_open = pane->open; ws->rpane_position = pane->user_position; prefs_set( "WORKSPACE_RPANE_OPEN", "%d", ws->rpane_open ); prefs_set( "WORKSPACE_RPANE_POSITION", "%d", ws->rpane_position ); iobject_changed( IOBJECT( ws ) ); } } static gboolean workspaceview_filedrop( Workspaceview *wview, const char *filename ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); gboolean result; result = workspaceview_load( ws, filename ); if( result ) symbol_recalculate_all(); return( result ); } static void workspaceview_column_new_action_cb2( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); if( !workspace_column_new( ws ) ) iwindow_alert( GTK_WIDGET( wview ), GTK_MESSAGE_ERROR ); } static void workspaceview_group_action_cb2( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); workspace_selected_group( ws ); } static void workspaceview_next_error_action_cb2( GtkWidget *wid, GtkWidget *host, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); if( !workspace_next_error( ws ) ) { error_top( _( "No errors in tab." ) ); error_sub( "%s", _( "There are no errors (that I can see) " "in this tab." ) ); iwindow_alert( GTK_WIDGET( wview ), GTK_MESSAGE_INFO ); } } static void workspaceview_init( Workspaceview *wview ) { GtkAdjustment *hadj; GtkAdjustment *vadj; Panechild *panechild; GtkWidget *ebox; wview->fixed = NULL; wview->window = NULL; wview->timer = 0; wview->u = 0; wview->v = 0; wview->dragging = FALSE; wview->drag_x = 0; wview->drag_y = 0; wview->vp.left = 0; wview->vp.top = 0; wview->vp.width = 0; wview->vp.height = 0; wview->width = -1; wview->height = -1; wview->bounding.left = 0; wview->bounding.top = 0; wview->bounding.width = 0; wview->bounding.height = 0; wview->next_x = 3; wview->next_y = 3; wview->context = NULL; wview->watch_changed_sid = g_signal_connect( main_watchgroup, "watch_changed", G_CALLBACK( workspaceview_watch_changed_cb ), wview ); wview->rpane = pane_new( PANE_HIDE_RIGHT ); g_signal_connect( wview->rpane, "changed", G_CALLBACK( workspaceview_rpane_changed_cb ), wview ); gtk_box_pack_start( GTK_BOX( wview ), GTK_WIDGET( wview->rpane ), TRUE, TRUE, 2 ); gtk_widget_show( GTK_WIDGET( wview->rpane ) ); wview->lpane = pane_new( PANE_HIDE_LEFT ); g_signal_connect( wview->lpane, "changed", G_CALLBACK( workspaceview_lpane_changed_cb ), wview ); gtk_paned_pack1( GTK_PANED( wview->rpane ), GTK_WIDGET( wview->lpane ), TRUE, FALSE ); gtk_widget_show( GTK_WIDGET( wview->lpane ) ); /* Ask for our own window so we can spot events on the window * background. */ wview->fixed = gtk_fixed_new(); gtk_widget_add_events( GTK_WIDGET( wview->fixed ), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ); gtk_fixed_set_has_window( GTK_FIXED( wview->fixed ), TRUE ); wview->window = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( wview->window ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( wview->window ), wview->fixed ); gtk_viewport_set_shadow_type( GTK_VIEWPORT( GTK_BIN( wview->window )->child ), GTK_SHADOW_NONE ); gtk_signal_connect( GTK_OBJECT( wview->window ), "scroll_event", GTK_SIGNAL_FUNC( workspaceview_scroll_event_cb ), wview ); gtk_signal_connect( GTK_OBJECT( wview->fixed ), "realize", GTK_SIGNAL_FUNC( workspaceview_realize_cb ), wview ); gtk_signal_connect( GTK_OBJECT( wview->fixed ), "event", GTK_SIGNAL_FUNC( workspaceview_fixed_event_cb ), wview ); gtk_widget_add_events( GTK_WIDGET( wview->fixed ), GDK_BUTTON_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ); hadj = gtk_scrolled_window_get_hadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); vadj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); gtk_signal_connect( GTK_OBJECT( hadj ), "value_changed", GTK_SIGNAL_FUNC( workspaceview_scroll_adjustment_cb ), wview ); gtk_signal_connect( GTK_OBJECT( hadj ), "changed", GTK_SIGNAL_FUNC( workspaceview_scroll_adjustment_cb ), wview ); gtk_signal_connect( GTK_OBJECT( vadj ), "value_changed", GTK_SIGNAL_FUNC( workspaceview_scroll_adjustment_cb ), wview ); gtk_signal_connect( GTK_OBJECT( vadj ), "changed", GTK_SIGNAL_FUNC( workspaceview_scroll_adjustment_cb ), wview ); /* We can't use gtk_container_set_focus_hadjustment() etc. since our * workspace contains a lot of nested structures, and hadjustment() * only works for single-layer things. Instead, do focus scrolling * ourselves .. see rowview.c. */ gtk_paned_pack2( GTK_PANED( wview->lpane ), GTK_WIDGET( wview->window ), TRUE, FALSE ); /* Toolkit Browser pane. */ panechild = panechild_new( wview->rpane, _( "Toolkit Browser" ) ); /* Have to put toolkitbrowser in an ebox so the search entry gets * clipped to the pane size. */ ebox = gtk_event_box_new(); gtk_container_add( GTK_CONTAINER( panechild ), GTK_WIDGET( ebox ) ); gtk_widget_show( ebox ); wview->toolkitbrowser = toolkitbrowser_new(); gtk_container_add( GTK_CONTAINER( ebox ), GTK_WIDGET( wview->toolkitbrowser ) ); gtk_widget_show( GTK_WIDGET( wview->toolkitbrowser ) ); /* Workspace-local defs pane. */ panechild = panechild_new( wview->lpane, _( "Tab Definitions" ) ); wview->workspacedefs = workspacedefs_new(); gtk_container_add( GTK_CONTAINER( panechild ), GTK_WIDGET( wview->workspacedefs ) ); gtk_widget_show( GTK_WIDGET( wview->workspacedefs ) ); filedrop_register( GTK_WIDGET( wview ), (FiledropFunc) workspaceview_filedrop, wview ); wview->popup = popup_build( _( "Workspace menu" ) ); popup_add_but( wview->popup, _( "New C_olumn" ), POPUP_FUNC( workspaceview_column_new_action_cb2 ) ); wview->popup_jump = popup_add_pullright( wview->popup, _( "Jump to _Column" ) ); menu_add_sep( wview->popup ); popup_add_but( wview->popup, _( "_Group Selected" ), POPUP_FUNC( workspaceview_group_action_cb2 ) ); menu_add_sep( wview->popup ); popup_add_but( wview->popup, STOCK_NEXT_ERROR, POPUP_FUNC( workspaceview_next_error_action_cb2 ) ); popup_attach( wview->fixed, wview->popup, wview ); gtk_widget_show_all( wview->window ); } GtkType workspaceview_get_type( void ) { static GtkType type = 0; if( !type ) { static const GtkTypeInfo info = { "Workspaceview", sizeof( Workspaceview ), sizeof( WorkspaceviewClass ), (GtkClassInitFunc) workspaceview_class_init, (GtkObjectInitFunc) workspaceview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; type = gtk_type_unique( TYPE_VIEW, &info ); } return( type ); } View * workspaceview_new( void ) { Workspaceview *wview = gtk_type_new( TYPE_WORKSPACEVIEW ); return( VIEW( wview ) ); } void workspaceview_set_label( Workspaceview *wview, GtkWidget *label, GtkWidget *padlock, GtkWidget *alert ) { g_assert( !wview->label ); g_assert( !wview->padlock ); g_assert( !wview->alert ); wview->label = label; wview->padlock = padlock; wview->alert = alert; } ================================================ FILE: src/workspaceview.h ================================================ /* a view of a workspace */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ #define TYPE_WORKSPACEVIEW (workspaceview_get_type()) #define WORKSPACEVIEW( obj ) \ (GTK_CHECK_CAST( (obj), TYPE_WORKSPACEVIEW, Workspaceview )) #define WORKSPACEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_CAST( (klass), \ TYPE_WORKSPACEVIEW, WorkspaceviewClass )) #define IS_WORKSPACEVIEW( obj ) (GTK_CHECK_TYPE( (obj), TYPE_WORKSPACEVIEW )) #define IS_WORKSPACEVIEW_CLASS( klass ) \ (GTK_CHECK_CLASS_TYPE( (klass), TYPE_WORKSPACEVIEW )) /* Column margins. */ #define WORKSPACEVIEW_MARGIN_LEFT (5) #define WORKSPACEVIEW_MARGIN_TOP (5) struct _Workspaceview { View view; GtkWidget *fixed; /* GtkFixed for tally */ GtkWidget *window; /* ScrolledWindow holding fixed */ Toolkitbrowser *toolkitbrowser; Workspacedefs *workspacedefs; GtkWidget *label; /* Tab label */ GtkWidget *padlock; /* The padlock icon */ GtkWidget *alert; /* The alert icon */ /* Left and right panes ... program window and toolkit browser. */ Pane *lpane; Pane *rpane; GtkWidget *popup; GtkWidget *popup_jump; /* Background window scroll. */ guint timer; int u; /* Set by columnview for bg scroll */ int v; /* Middle button drag scroll. */ gboolean dragging; int drag_x; int drag_y; /* Geometry. */ Rect vp; /* Viewport pos and size */ int width; /* Size of fixed area */ int height; Rect bounding; /* Bounding box of columnviews */ /* Placement hints for new columns. */ int next_x; int next_y; /* Context we use to change cursor shape. */ iWindowCursorContext *context; /* Follow prefs changes. */ guint watch_changed_sid; /* Only show the compat warning once. */ gboolean popped_compat; }; typedef struct _WorkspaceviewClass { ViewClass parent_class; /* My methods. */ } WorkspaceviewClass; void workspaceview_scroll( Workspaceview *wview, int x, int y, int w, int h ); void workspaceview_scroll_background( Workspaceview *wview, int u, int v ); void workspaceview_set_cursor( Workspaceview *wview, iWindowShape shape ); GtkType workspaceview_get_type( void ); View *workspaceview_new( void ); void workspaceview_set_label( Workspaceview *wview, GtkWidget *label, GtkWidget *padlock, GtkWidget *alert ); ================================================ FILE: test/Makefile.am ================================================ EXTRA_DIST = \ workspaces \ extras \ images TESTS = \ test_all.sh ================================================ FILE: test/extras/test_magick2.ws ================================================ ================================================ FILE: test/extras/test_recomp_order.ws ================================================ ================================================ FILE: test/extras/test_transform.ws ================================================ ================================================ FILE: test/test_all.sh.in ================================================ #!/bin/sh # set -x main_result=0 test_result() { if [ $1 = 0 ]; then echo ok else echo failed main_result=1 fi } test_result_fail() { if [ $1 = 0 ]; then echo error: did not fail main_result=1 else echo fail expected fi } # run the test workspaces test_workspaces() { for i in @TOP_SRCDIR@/test/workspaces/*.ws; do base=$(basename $i) echo -n "testing $base ... " @TOP_SRCDIR@/src/nip2 --prefix=@TOP_SRCDIR@ --test "$i" result=$? # test_fail.ws is supposed to fail if [ x"$base" = x"test_fail.ws" ]; then test_result_fail $result else test_result $result fi done } # run the test defs test_defs() { for i in @TOP_SRCDIR@/test/workspaces/*.def; do base=$(basename $i) echo -n "testing $base ... " @TOP_SRCDIR@/src/nip2 --prefix=@TOP_SRCDIR@ --test $i test_result $? done } # load all the example workspaces too test_examples() { for i in @TOP_SRCDIR@/share/nip2/data/examples/*/*.ws; do base=$(basename $i) # have to skip these two, they use a non-free plugin if test x"$base" = x"framing.ws" ; then continue fi if test x"$base" = x"registering.ws" ; then continue fi echo -n "testing $base ... " @TOP_SRCDIR@/src/nip2 --prefix=@TOP_SRCDIR@ --test $i test_result $? done } test_workspaces test_defs test_examples echo "repeating tests with the vectorising system disabled" export IM_NOVECTOR=1 test_workspaces test_defs test_examples exit $main_result ================================================ FILE: test/workspaces/big_and_small_disks.ws ================================================ ================================================ FILE: test/workspaces/test_colour.ws ================================================ ================================================ FILE: test/workspaces/test_conv.ws ================================================ ================================================ FILE: test/workspaces/test_fail.ws ================================================ ================================================ FILE: test/workspaces/test_filter.ws ================================================ ================================================ FILE: test/workspaces/test_fourier.ws ================================================ ================================================ FILE: test/workspaces/test_histogram.ws ================================================ ================================================ FILE: test/workspaces/test_image.ws ================================================ ================================================ FILE: test/workspaces/test_math.ws ================================================ ================================================ FILE: test/workspaces/test_matrix.ws ================================================ ================================================ FILE: test/workspaces/test_snip.def ================================================ // all the image formats we test, and their matching number format // we can't use uchar as a number format directly since it'll become 'a' or // whatever and arithmetic ops will start failing fmts = [ ["uchar", cast_unsigned_char, cast_unsigned_int @ cast_unsigned_char], ["char", cast_signed_char, cast_signed_int @ cast_signed_char], ["ushort", cast_unsigned_short, cast_unsigned_short], ["short", cast_signed_short, cast_signed_short], ["uint", cast_unsigned_int, cast_unsigned_int], ["int", cast_signed_int, cast_signed_int], ["float", cast_float, cast_float], ["double", cast_double, cast_double], ["complex", cast_complex, cast_complex], ["dcomplex", cast_double_complex, cast_double_complex] ]; // we need a to_real that does images as well to_real x = abs x, is_complex x = mean x, is_Image x = x; // numbers we test numbers = [-10, 0, 1, 10, 3.1415927]; test_unary op_name fn = foldr1 logical_and [test fname ifmt nfmt x :: [fname, ifmt, nfmt] <- fmts; x <- numbers] { // image == number can fail due to rounding differences test fname ifmt nfmt x = true, abs (image - number) < 0.001 = error (join_sep " " (map print ["unary", fname, op_name, x, "==", image, number])) { image = (to_real @ fn @ ifmt @ to_image) x; number = (to_real @ fn @ nfmt) x; } } test_binary op_name fn = foldr1 logical_and [test fname ifmt nfmt x y :: [fname, ifmt, nfmt] <- fmts; x <- numbers; y <- numbers] { // image == number can fail due to rounding differences test fname ifmt nfmt x y = true, abs (image - number) < 0.001 = error (join_sep " " (map print ["binary", fname, x, op_name, y, "==", image, number])) { image = to_real (fn ((ifmt @ to_image) x) ((ifmt @ to_image) y)); number = to_real (fn (nfmt x) (nfmt y)); } } tests = [ test_binary "add" add, test_binary "subtract" subtract, test_binary "multiply" multiply, test_binary "divide" test_div, test_unary "square" square, test_unary "constant plus" (add 12), test_unary "plus constant" (converse add 12), test_unary "divided by constant" (converse test_div 3), test_unary "multiply constant" (multiply 7), test_unary "constant multiplied by" (converse multiply 7), test_unary "constant subtracted from" (subtract 4), test_unary "subtract constant" (converse subtract 4), "" ++ "a" == "a", hd [1, error "nope"] == 1 ] { // libvips divide returns 0 for divide by zero test_div a b = 0, is_real b && b == 0 = 0, is_complex b && re b == 0 && im b == 0 = divide a b; } main = "all tests pass", fail == [] = "failed: " ++ join_sep ", " (map print fail_numbers) { numbered = zip2 tests [1..]; fail = filter (not @ extract 0) numbered; fail_numbers = map (extract 1) failed; } ================================================ FILE: test/workspaces/test_stats.ws ================================================ ================================================ FILE: test/workspaces/test_tasks.ws ================================================ ================================================ FILE: test/workspaces/test_widgets.ws ================================================